<?php

namespace control;
/*
*生产计划类
*/

class machiningOrderClass extends adIndexClass
{
    private const page = 'machiningOrder';
    private const table = 'machining_order';
    private const tableMx = 'machining_order_detail';
    private const table_material = 'machining_order_mx';
    private const title = '生产计划';
    private array $stateOption = ['草稿', '待确认', '已确认'];

    /**
     * 字段初始化
     */
    public function fieldInit($fun): array
    {
        switch ($fun) {
            case 'semiOrderSearch':
                $tr = [
                    'recordDate'          => [
                        'name'  => "单据日期",
                        'width' => 150,
                        'sort' => 1,
                    ],
                    'orderNo' => [
                        'name'  => "单据编号",
                        'width' => 150,
                        'sort' => 1,
                    ],
                    'subject'  => [
                        'name'  => "存货类别",
                        'width' => 150,
                    ],
                    'code'  => [
                        'name'  => '物料编码',
                        'width' => 150,
                        'sort' => 1,
                    ],
                    'name'         => [
                        'name'  => '物料名称',
                        'width' => 150,
                    ],
                    'model'          => [
                        'name'  => '规格型号',
                        'width' => 150,
                    ],
                    'unit'          => [
                        'name'  => '单位',
                        'width' => 60,
                    ],
                    'num'          => [
                        'name'  => '需求数量',
                        'width' => 100,
                        'sort' => 1,
                    ],
                    'inventoryNum'          => [
                        'name'  => '库存数量',
                        'width' => 120,
                    ],
                    'inStockNum'          => [
                        'name'  => '已生产数量',
                        'width' => 100,
                    ],
                    'residue'          => [
                        'name'  => '剩余待生产数量',
                        'width' => 150,
                    ],
                    'source'          => [
                        'name'  => '来源',
                        'width' => 150,
                    ],
                    'supplyControl'          => [
                        'name'  => '供应控制',
                        'width' => 120,
                    ]
                ];
                break;
            case 'qualityTrackSearch':
                $tr = [
                    'type'          => [
                        'name'  => "质检类型",
                        'width' => 150,
                    ],
                    'time' => [
                        'name'  => "单据日期",
                        'width' => 150,
                        'sort' => 1,
                    ],
                    'orderNo'  => [
                        'name'  => "单据编号",
                        'width' => 150,
                    ],
                    'lineName'  => [
                        'name'  => '生产线',
                        'width' => 100,
                    ],
                    'processName'  => [
                        'name'  => '工序',
                        'width' => 100,
                    ],
                    'code'  => [
                        'name'  => '物料编码',
                        'width' => 150,
                        'sort' => 1,
                    ],
                    'name'         => [
                        'name'  => '物料名称',
                        'width' => 150,
                    ],
                    'model'          => [
                        'name'  => '规格型号',
                        'width' => 150,
                    ],
                    'unit'          => [
                        'name'  => '单位',
                        'width' => 60,
                    ],
                    'batch'          => [
                        'name'  => '生产批次',
                        'width' => 100,
                        'sort' => 1,
                    ],
                    'num'          => [
                        'name'  => '完工数量',
                        'width' => 120,
                        'sort' => 1,
                    ],
                    'passNum'          => [
                        'name'  => '合格数量',
                        'width' => 100,
                        'sort' => 1,
                    ],
                    'badNum'          => [
                        'name'  => '不良数量',
                        'width' => 100,
                    ],
                    'state'          => [
                        'name'  => '状态',
                        'width' => 150,
                    ],
                    'checkTime'          => [
                        'name'  => '质检日期',
                        'width' => 120,
                        'sort' => 1,
                    ]
                ];
                break;
            default:
                $tr = [
                    'orderNo'          => [
                        'name'  => "单据编号",
                        'width' => 150,
                        'sort' => 1,
                    ],
                    'tag' => [
                        'name'  => "单据标签",
                        'width' => 150,
                    ],
                    'line'  => [
                        'name'  => "生产线",
                        'width' => 150,
                    ],
                    'subject'  => [
                        'name'  => '存货类型',
                        'width' => 100,
                    ],
                    'code'  => [
                        'name'  => '物料编码',
                        'width' => 150,
                        'sort' => 1,
                    ],
                    'materialName'         => [
                        'name'  => '物料名称',
                        'width' => 150,
                    ],
                    'model'          => [
                        'name'  => '规格型号',
                        'width' => 150,
                    ],
                    'unit'          => [
                        'name'  => '单位',
                        'width' => 60,
                    ],
                    'num'          => [
                        'name'  => '生产数量',
                        'width' => 100,
                        'sort' => 1,
                    ],
                    'inStockNum'          => [
                        'name'  => '已生产入库数量',
                        'width' => 120,
                    ],
                    'notInNum'          => [
                        'name'  => '未入库数量',
                        'width' => 100,
                    ],
                    'completionRate'          => [
                        'name'  => '生产完成率',
                        'width' => 100,
                    ],
                    'relativeNo'          => [
                        'name'  => '关联销售订单号',
                        'width' => 150,
                    ],
                    'startDate'          => [
                        'name'  => '计划开工时间',
                        'width' => 120,
                        'sort' => 1,
                    ],
                    'endDate'          => [
                        'name'  => '计划完工时间',
                        'width' => 120,
                        'sort' => 1,
                    ],
                    'recordDate'          => [
                        'name'  => '单据日期',
                        'width' => 120,
                        'sort' => 1,
                    ],
                    'state'          => [
                        'name'  => '状态',
                        'width' => 100,
                    ]
                ];
        }
        return $tr;
    }

    /*列表页-查询条件*/
    public function homeOptions($isAsync = true)
    {
        //生成查询表单
        $html = $this->tableShow([
            [
                [
                    "单据编号" => text(array("name" => "orderNo")),
                    "标签"   => text(array("name" => "tags")),
                    "物料编码"   => text(array("name" => "code")),
                ],
                [
                    "物料名称"   => text(array("name" => "materialName")),
                    '生产线' => selectMultiple(["name" => "lineId", 'table' => "product_line", 'sqlId' => 'id', 'where' => "coid = '" . self::$coid . "'", 'sqlField' => 'name']),
                    '单据日期'  => text(array("name" => "recordDate", 'laydate' => ['type' => 'date', 'range' => '~'])),
                ],
                [
                    "计划开工时间" => text(array("name" => "startTime", 'laydate' => ['type' => 'date', 'range' => '~'])),
                    "状态" => select(["name" => "state", 'option' => $this->stateOption]),
                ]
            ]
        ],1);
        //返回
        if (!$isAsync) {
            return $html;
        } else {
            return json_encode(['html' => $html], 320);
        }
    }

    /*列表-查询条件*/
    public function home()
    {
        $html = $this->homeOptions(false);
        //控件
        $button = "";
        $url = root . "control/adIndex.html?cla=" . self::page;
        $isEdit = $this->power(self::page, "edit");
        if ($isEdit) {
            $button .= "<span onclick=\"newIframe('" . self::title . "详情','" . $url . "&fun=edit')\" class='spanButton'>新增</span>\n
                        <span onclick=\"layer('orderTag','batchAddTags',{'table':'".self::table."','title':'".self::title."'})\" class='spanButton'>批量打标签</span>\n";
            $button .= "<span class='spanButton' onclick=\"layer('{$this->get('cla')}', 'operate', {'act': 'close'})\">批量关闭</span>\n";
        }
        if ($this->power(self::page, "del")) {
            $button .= "<span class='spanButton' onclick=\"layer('{$this->get('cla')}', 'operate', {'act': 'batchDel'})\">删除</span>\n";
        }
        if ($this->power(self::page, "print")) {
            $button .= " <span onclick=\"batchPrint('" . $this->get('cla') . "')\" class='spanButton'>批量打印</span>\n";
        }
        if ($this->power(self::page, 'import')) {
            $button .="<span class='spanButton' onclick=\"layer('importTemplate','importFrame',{cla:'".self::page."'})\">导入</span>";
        }
        //返回
        return $this->homeFun($this->search($html, layout: 'pop', toolButton: $button));
    }

    /*客户列表-查询结果*/
    public function homeSearch()
    {
        //like查询数组
        $like = array(
            "a.orderNo" => $this->post('orderNo'),
            "b.name"    => $this->post('materialName'),
            "b.code"    => $this->post('code'),
        );
        //全等查询数组
        $equal = array(
            "a.state"     => $this->post('state'),
            "a.lineId"    => $this->post('lineId'),
            'a.coid'      => self::$coid,
            'a.orderType' => '生产计划'
        );
        $and   = '';
        if ($this->post('startTime')) {
            $and .= self::timeComparison($this->post('startTime'), field: 'startDate');
        }
        if ($this->post('endTime')) {
            $and .= self::timeComparison($this->post('endTime'), field: 'endDate');
        }
        if ($this->post('recordDate')) {
            $and .= self::timeComparison($this->post('recordDate'), field: 'recordDate');
        }
        if ($this->post('tags')) {
            $and     .= " and (";
            $tagsArr = explode('，', $this->post('tags'));
            foreach ($tagsArr as $index => $tag) {
                $and .= $index == 0 ? "a.tags like '%" . $tag . "%'" : " and a.tags like '%" . $tag . "%'";
            }
            $and .= ")";
        }
        //排序
        $orderBy = $this->listSort(array(
            "search"  => $this->post('list_sort_key') ?: 'a.recordDate desc,a.time',
            "sort" => $this->post('list_sort') ?: "asc",
            "array"   => array(),
        ));
        //获取查询结果
        $where = search(array("like" => $like, "=" => $equal)) . $and;
        $page  = $this->page(array(
            "table" => self::table . " as a left join " . self::table_material . ' as c on c.orderId = a.id left join material as b on c.materialId = b.id ',
            'field' => "c.*,b.subject,b.name as materialName,b.code,b.model,a.isClose,a.state,a.lineId,a.orderNo,a.type,a.startDate,a.endDate,a.recordDate,a.id as orderId",
            "where" => $where,
            "order" => $orderBy['orderBy']
        ));
        //获取自定义字段参数
        $installSearch = $this->installSearch();
        $body          = [];
        //表格控件区
        $liButton = array(
            $this->checkAll("listForm", "listId")
        );
        //获取记录
        $url = root . "control/adIndex.html?cla=" . self::page;
        $isEdit = $this->power(self::page, "edit");
        foreach ($page['array'] as $array) {
            //列表控件
            $dominate = array(
                "box"    => array("name" => "listId", "value" => $array['id']?:$array['orderId']),
                "edit"   => array("title" => "编辑", "click" => "newIframe('" . self::title . "详情','" . $url . "&fun=edit&id=" . $array['orderId'] . "')"),
                "detail" => array("title" => self::title . "详情", "click" => "newIframe('" . self::title . "详情','" . $url . "&fun=detail&id=" . $array['orderId'] . "')"),
                'print'  => array('title' => '打印', 'click' => "window.open('" . root . "control/adIndex.html?cla=" . self::page . "&fun=printPage&id={$array['orderId']}')"),
                'track'  => array("title" => "单据追踪", "click" => "newIframe('单据追踪','" . root . "control/adIndex.html?cla=orderTrack&fun=home&orderType=生产计划单&id=" . $array['orderId'] . "')"),
            );
            if ($array['state'] == '已确认' || !$isEdit) {
                unset($dominate['edit']);
            }
            $closeSign = $array['isClose'] ? "<span class='no-choose'></span>" : '';//关闭单据颜色显示标志
            //已生产入库的数量
            $inStockNum = $this->inStockNum($array['id'], $array['unit']);
            $liButton[] = $this->dominate($dominate) . $closeSign;
            //本行初始化
            $row = [];
            foreach ($installSearch['key'] as $key) {
                switch ($key) {
                    case 'tag':
                        $tag         = orderTagClass::getTagName($array['orderId']);
                        $row[] = $tag;
                        break;
                    case 'line':
                        $line        = query('product_line', "id = '$array[lineId]'");
                        $row[] = $line['name'];
                        break;
                    case 'num':
                        $row[] = self::lostZero($array['num'],'numPrecision');
                        break;
                    case 'inStockNum':
                        $row[] = self::lostZero($inStockNum, 'numPrecision');
                        break;
                    case 'notInNum':
                        $row[] = self::lostZero($array['num'] - $inStockNum, 'numPrecision');
                        break;
                    case 'completionRate':
                        $row[] = self::formatRatio($inStockNum, $array['num']);
                        break;
                    case 'relativeNo':
                        $relativeNo  = '';
                        if ($array['sourceTable'] == 'product_demands') {
                            $productDemand = query('product_demands', "id = '$array[sourceId]'");
                            $sale_order    = query('sale_order', "id = '$productDemand[sourceId]'");
                            $relativeNo    = $sale_order['orderNo'];
                        } elseif ($array['sourceTable'] == 'sale_refund_order_detail') {
                            $saleRefundOrderMx = query('sale_refund_order_detail', "id = '$array[sourceId]'");
                            $saleRefundOrder   = query('sale_refund_order', "id = '$saleRefundOrderMx[orderId]'");
                            $relativeNo        = $saleRefundOrder['orderNo'];
                        } elseif ($array['sourceTable'] == 'sale_order_detail') {
                            $saleOrderMx = query('sale_order_detail', "id = '$array[sourceId]'");
                            $saleOrder   = query('sale_order', "id = '$saleOrderMx[orderId]'");
                            $relativeNo  = $saleOrder['orderNo'];
                        }
                        $row[] = $relativeNo;
                        break;
                    case 'startDate':
                    case 'endDate':
                        $row[] = self::null($array[$key]);
                        break;
                    default:
                        $row[] = isset($array[$key]) && $array[$key] ? $array[$key] : '';
                }
            }
            $body[] = $row;
        }

        $productNum = $inStock = 0;
        $all        = self::sqlFind($page['fullSql']);
        foreach ($all as $array) {
            $inStock    += $this->inStockNum($array['id'], $array['unit']);
            $productNum += $array['num'];
        }
        //控件
        $button = "";
        $total = [
            'num' => self::lostZero($productNum, 'numPrecision'),
            'inStockNum' => self::lostZero($inStock, 'numPrecision'),
        ];
        //生成页面
        $li = $this->li(array(
            "body" => $body,
            "install" => $installSearch,
            "button" => array("html" => $liButton),
            "orderBySign" => $orderBy,
            "page" => $page['para'],
            "total" => $total,
        ));
        //返回
        $json['html'] = $page['hint'] . $button . "<form name='listForm'>" . $li . "</form>" . self::closeJs() . $page['button'] . clear;
        return json_encode($json, 320);
    }

    /**
     * 下载模板
     */
    public function downloadModel()
    {
        $templateId = $this->get('templateId');
        if (!$this->power(self::page, 'import')) {
            $_SESSION['warn'] = '你还没有导入的权限';
        }  else {
            $exampleData[] = [
                '2023-02-24', 'SCX001', 'A车间', '2023-02-24', '2023-02-25', '样例数据', '产品明细', 'CP0001', 'A产品', '包', 10, 'PC202302240001', 'BOM202302160001', 'BOM1', 'POP0001', 'A产品工艺路线', '测试数据'
            ];
            $exampleData[] = [
                '', '', '', '', '', '', '所需物料', 'YL001', 'A原料', 'kg', 1, 0, '', '', '', '', '测试数据'
            ];
            $exampleData[] = [
                '', '', '', '', '', '', '所需物料', 'YL002', 'B原料', 'kg', 1, 0, '', '', '', '', '测试数据'
            ];

            $excel    = $this->excelHead();
            $fileName = self::title.'导入模板-'.importTemplateClass::getName($templateId).'.xlsx';
            $filePath = $excel->fileName($fileName, 'sheet1');
            $format = new \Vtiful\Kernel\Format($filePath->getHandle());
            $templateField = importTemplateClass::templateField(self::page, 'importFieldInit', id:$templateId);
            $index         = 0;
            foreach ($templateField as $k => $array) {
                if ($array['display']){
                    $backgroundStyle=null;
                    if (importTemplateClass::getIsMustValue($this->importFieldInit(), $array['columnName'])){
                        //必填项判断
                        $backgroundStyle  = $format->background(
                            0xFFC04B
                        )->toResource();
                    }
                    $filePath->insertText(0, $index, $array['aliasName'],'',$backgroundStyle);
                    foreach ($exampleData as $y=>$example){
                        $filePath->insertText($y+1, $index, $example[$k]??'');
                    }
                    $index++;
                }
            }
            $text   = $this->control['name'] . "下载了" . self::title . "导入模板";
            $this->logText(["type" => self::page, "text" => $text]);
            $this->excelOutPut($filePath,$fileName);
            exit(0);
        }
        header("Location:" . getenv("HTTP_REFERER"));
    }

    /*数据导入*/
    public function importData()
    {
        $templateId = $this->post('templateId');
        if (!$this->power(self::page, 'import')) {
            $json['warn'] = '你还没有导入的权限';
        }  else {
             $fomatArray = [];//导入数据格式同一为TYPE_STRING
            foreach ($this->importFieldInit() as $value){
                $fomatArray[] = \Vtiful\Kernel\Excel::TYPE_STRING;
            }
            $data = $this->importHead(format: $fomatArray);
            $x              = 0;
            $text           = '';
            $machiningOrder = [];
            $headArray = [];
            $row_list=0;
            foreach ($data as $i => $row) {
                $row_list++;
                if ($i==0){
                    $templateField = importTemplateClass::templateField(self::page, 'importFieldInit', id:$templateId);
                    $headArray = importTemplateClass::combinHead($templateField,$row);
                }
                if ($i > 0) {
                    $rowData = formSubArray($row);
                    //只接收模板中选中的字段的值
                    $cell = [];
                    foreach ($headArray as $index=>$headName){
                        $cell[$headName] = $rowData[$index]??'';
                    }
                    //初始化未被模板需要的字段的值
                    foreach ($this->importFieldInit() as $value){
                        $cell[$value['columnName']] = $cell[$value['columnName']]??'';
                    }
                    if (!empty($cell['单据日期'])) {
                        $product_line = query('product_line', "coid = '" . self::$coid . "' and state = '启用' and orderNo = '$cell[生产线编号]'");
                        $field        = [
                            'orderType'  => '生产计划',
                            'recordDate' => self::importDateFormat($cell['单据日期']),
                            'type'       => '生产加工',
                            'lineId'     => $product_line['id'],
                            'startDate'  => self::importDateFormat($cell['计划开始时间']),
                            'endDate'    => self::importDateFormat($cell['计划完工时间']),
                            'text'       => $cell['单据备注'],
                        ];
                        $annotation   = [
                            'lineId'     => '生产线',
                            'recordDate' => '单据日期',
                            'startDate'  => '计划开始时间',
                            'endDate'    => '计划完工时间',
                        ];
                        $rules        = [
                            'lineId'     => 'required',
                            'recordDate' => 'required|date',
                            'startDate'  => 'required|date',
                            'endDate'    => 'required|date',
                        ];
                        // 校验是否符合规则
                        $vt     = new \verify();
                        $result = $vt->setAliases($annotation)->validate($field, $rules)->isOk();
                        if ($result === false) {
                            $text .= "\n第【{$row_list}】行，" . $vt->getErrs()[0];
                        } else {
                            $pass = true;
                            if ($pass) {
                                // 新增
                                $userNo = codeTemplateClass::generateCode(self::page, [
                                    'prefix' => 'SCJH' . date('Ymd', strtotime($field['recordDate'])),
                                    'table'  => self::table,
                                    'field'  => 'orderNo',
                                    'where'  => "and coid = '" . self::$coid . "'",
                                ], $field);
                                $field  = array_merge($field, [
                                    'id'      => suiji(),
                                    'orderNo' => $userNo,
                                    'state'   => '草稿',
                                    'coid'    => self::$coid,
                                    'stid'    => $this->control['stid'],
                                ]);
                                $warn   = static::insert([
                                    "table" => self::table,
                                    "field" => $field,
                                ]);
                                if (in_array($warn, ["新增成功", "更新成功"])) {
                                    $machiningOrder = $field;
                                    self::insertOperateLog($field['id'],self::title,"新建");
                                    $x++;
                                } else {
                                    $machiningOrder = [];
                                    $text           .= "\n第【{$row_list}】行，插入主数据失败，" . $warn;
                                }
                            }
                        }
                    }
                    if (isset($machiningOrder['id']) && !empty($machiningOrder['id'])) {
                        if ($cell['明细类型'] == '所需物料') {
                            $material    = query('material', "coid = '" . self::$coid . "' and code = '$cell[产品编码]'");
                            //明细
                            $mxField = [
                                'id'            => suiji(),
                                'orderId'       => $machiningOrder['id'],
                                'coid'          => self::$coid,
                                'materialId'    => $material['id'],
                                'unit'          => $cell['单位'],
                                'num'           => $cell['计划数量/额定数量'],
                                'quantity'      => self::formattingFigure($cell['计划数量/额定数量'] * (1 + floatval($cell['生产批次/损耗率']) / 100), 'numPrecision'),
                                'loss'          => floatval($cell['生产批次/损耗率']) / 100,
                                'text'          => $cell['备注'],
                            ];
                            $warn    = static::insert([
                                "table" => self::tableMx,
                                "field" => $mxField,
                            ]);
                            if (in_array($warn, ["新增成功", "更新成功"])) {
                                $text .= "\n第【{$row_list}】行，【所需物料】导入成功";
                            } else {
                                $text .= "\n第【{$row_list}】行，【所需物料】导入失败：插入数据库失败";
                            }
                        } else {
                            $material    = query('material', "coid = '" . self::$coid . "' and code = '$cell[7]'");
                            $bom         = query('bom', "coid = '" . self::$coid . "' and orderNo = '$cell[12]'");
                            $routing     = query('routing', "coid = '" . self::$coid . "' and orderNo = '$cell[14]'");
                            //明细
                            $mxField = [
                                'id'            => suiji(),
                                'orderId'       => $machiningOrder['id'],
                                'coid'          => self::$coid,
                                'materialId'    => $material['id'],
                                'num'           => $cell['计划数量/额定数量'],
                                'batch'         => $cell['生产批次/损耗率'],
                                'unit'          => $cell['单位'],
                                'bomId'         => $bom['id'],
                                'routingId'     => $routing['id'],
                                'text'          => $cell['备注'],
                            ];
                            $warn    = static::insert([
                                "table" => self::table_material,
                                "field" => $mxField,
                            ]);
                            if (in_array($warn, ["新增成功", "更新成功"])) {
                                $text .= "\n第【{$row_list}】行，【产品明细】导入成功";
                            } else {
                                $text .= "\n第【{$row_list}】行，【产品明细】导入失败：插入数据库失败";
                            }
                        }
                    } else {
                        $text .= "\n第【{$row_list}】行，未找到单据";
                    }
                }
            }
            $total = count($data)-1;
            $json['data'] = [
                'text'=>$text,
                'title'=>"您导入了{$total}条数据，成功{$x}条，失败".($total-$x)."条",
            ];
            $text = $this->control['name'] . '导入了文件：' . $_FILES['file']['name'] . '：' . $text;
            $this->logText(["type" => self::page, "text" => $text]);
            $json['warn'] = 2;
        }
        return json_encode($json,320);
    }

    /**
     * 批量添加标签编辑
     */
    public function closeEdit(): bool|string
    {
        $listId = $this->post('listId');
        if (!$this->power(self::page, "edit")) {
            $json['warn'] = "你没有编辑" . self::title . "的权限";
        } elseif (empty($listId) || !is_array($listId)) {
            $json['warn'] = '您还没有选择单据';
        } else {
            $x    = 0;
            $text = '';
            $all  = find(array(
                'table' => 'machining_order_mx',
                'field' => "distinct orderId",
                "where" => "coid = '" . self::$coid . "' and id in ('" . implode("','", $listId) . "')"
            ));
            foreach ($all['array'] as $array) {
                $machiningOrder = query(self::table, "id = '$array[orderId]'");
                if ($machiningOrder['state'] == '已确认' && !$machiningOrder['isClose']) {
                    $warn = update(array(
                        'table' => self::table,
                        'field' => [
                            'isClose' => 1
                        ],
                        'where' => "id = '$machiningOrder[id]'"
                    ));
                    if ($warn == '更新成功') {
                        $x++;
                        materialDistributeClass::avgInventoryPrice($machiningOrder['id']);
                        self::insertOperateLog($machiningOrder['id'],self::title,"关闭");
                        $text .= "生产计划单【{$machiningOrder['orderNo']}】,ID：【{$machiningOrder['id']}】关闭成功<br>";
                    } else {
                        $text .= "生产计划单【{$machiningOrder['orderNo']}】,ID：【{$machiningOrder['id']}】关闭失败<br>";
                    }
                }
            }
            $this->logText(["type" => self::page, "text" => $this->control['name'] . "批量关闭生产计划单如下：" . $text]);
            $json['warn']     = 2;
            $_SESSION['warn'] = '成功关闭了' . $x . '个生产计划单';

        }
        return json_encode($json, 320);
    }

    /**
     * 计算完成率
     * @param $molecule
     * @param $denominator
     * @param int $decimal
     * @param bool $is_ratio
     * @return string
     */
    public static function formatRatio($molecule, $denominator, $decimal = 4, $is_ratio = true): string
    {
        $num = 0;
        if ($denominator > 0) {
            if (self::formattingFigure($molecule, 'numPrecision') == self::formattingFigure($denominator, 'numPrecision')) {
                $num =  100;
            }else {
                $num =  (floor($molecule / $denominator * pow(10, $decimal)) / 100);
            }
        }
       return $is_ratio? $num. '%' : $num;
    }

    /**
     * 已入库的生产数量
     * @param $machiningOrderMxId
     * @param $unit
     * @return float|int
     */
    public function inStockNum($machiningOrderMxId, $unit): float|int
    {
        $all = find(array('table' => 'machining_in_order_detail', 'where' => "sourceId = '$machiningOrderMxId' order by list"));
        $num = 0;
        foreach ($all['array'] as $array) {
            $inventory_detail = query('inventory_detail', "sourceId = '$array[id]'");
            $num              += machiningOrderClass::material_num_change($array['materialId'], $inventory_detail['unit'], $unit, $inventory_detail['num']);
        }
        return $num;
    }

    /**
     * 订单关闭颜色显示操作
     * @return string
     */
    public static function closeJs(): string
    {
        return <<<efo
<script>
$(function (){
    var li = $(".no-choose").parent().parent()
    li.css('color','#a3a3a3');
    li.attr('title','已关闭单据');
    //更新表控件区高亮样式
    var index = li.index();//当前控件索引
    var body = li.parent("ul").parent(".buttonBody").parent(".left").next(".right").children(".body");
    body.children("ul:nth-of-type("+(index+1)+")").css('color','#a3a3a3');
    body.children("ul:nth-of-type("+(index+1)+")").attr('title','已关闭单据');
})
</script>
efo;
    }


    /** 可用库存计算 可用库存 = 现存量 + 预计入库量 - 预计出库量
     * @param $materialId
     * @param $unit
     * @param string $warehouseId
     * @return float|int
     */
    public static function inventory($materialId, $unit, $warehouseId = ''): float|int
    {
        if (empty($materialId) || empty($unit)) {
            return 0;
        }
        $parameters   = optionSettingClass::getSetting('库存');
        $where        = '';
        $mrpWarehouse = [];
        if ($warehouseId) {
            $where .= " and warehouseId = '$warehouseId'";
        } else {
            //参与MRP运算仓库
            $mrpWarehouse = (array)$parameters['mrpWarehouse'];
            $where        .= " and warehouseId in ('" . implode("','", $mrpWarehouse) . "') ";
        }
        $material    = query('material', "id = '$materialId'", "unit");
        $num         = find(array('table' => 'inventory_record', 'where' => "coid = '" . self::$coid . "' and materialId = '$materialId'" . $where, 'sum' => "num"));
        $num         = self::material_num_change($materialId, $material['unit'], $unit, $num);
        //预计入库量
        $inArr      = [
            'machiningWaitIn' => '生产入库',
            'purchaseWaitIn' => '采购收货',
            'outsourceWaitIn' => '委外收货',
            'allocationWaitIn' => '调拨单入库',
            'otherWaitIn' => '其他入库'
        ];
        $estimateIn = $parameters['estimateIn'];
        foreach ((array)$estimateIn as $val) {
            switch ($val) {
                case 'allocationWaitIn':
                    $where  = $warehouseId ? " and a.inWarehouseId = '$warehouseId' " : " and a.inWarehouseId in ('" . implode("','", $mrpWarehouse) . "') ";
                    $result = find(array(
                        'table' => 'inventory_allot_detail as b join inventory_allot as a on b.orderId = a.id',
                        'where' => "a.coid = '" . self::$coid . "' and a.state = '待确认' and b.materialId = '$materialId'" . $where,
                        'field' => "b.num,b.unit"
                    ));
                    foreach ((array)$result['array'] as $array) {
                        $num += self::material_num_change($materialId, $array['unit'], $unit, $array['num']);
                    }
                    break;
                default:
                    $where  = " and a.type = '" . $inArr[$val] . "' and a.classify = 1 and a.state in ('待入库','部分入库') ";
                    $where  .= $warehouseId ? " and b.warehouseId = '$warehouseId' " : " and b.warehouseId in ('" . implode("','", $mrpWarehouse) . "') ";
                    $result = find(array(
                        'table' => 'inventory_detail as b join inventory as a on b.orderId = a.id',
                        'where' => "a.coid = '" . self::$coid . "' and b.materialId = '$materialId'" . $where,
                        'field' => "(b.orderNum - b.num) as num,b.unit"
                    ));
                    foreach ((array)$result['array'] as $array) {
                        $num += self::material_num_change($materialId, $array['unit'], $unit, $array['num']);
                    }
            }
        }
        //预计出库量
        $outArr      = [
            'sendWaitIn' => '销售发货',
            'pickWaitOut' => '生产领料',
            'ingOrderWaitOut' => '配料出库',
            'outsourceWaitOut' => '委外领料',
            'allocationWaitOut' => '调拨单出库',
            'otherWaitOut' => '其他出库'
        ];
        $estimateOut = $parameters['estimateOut'];
        foreach ((array)$estimateOut as $val) {
            switch ($val) {
                case 'allocationWaitOut':
                    $where  = $warehouseId ? " and a.outWarehouseId = '$warehouseId' " : " and a.outWarehouseId in ('" . implode("','", $mrpWarehouse) . "') ";
                    $result = find(array(
                        'table' => 'inventory_allot_detail as b join inventory_allot as a on b.orderId = a.id',
                        'where' => "a.coid = '" . self::$coid . "' and a.state = '待确认' and b.materialId = '$materialId'" . $where,
                        'field' => "b.num,b.unit"
                    ));
                    foreach ((array)$result['array'] as $array) {
                        $num -= self::material_num_change($materialId, $array['unit'], $unit, $array['num']);
                    }
                    break;
                default:
                    $where  = " and a.type = '" . $outArr[$val] . "' and a.classify = 0 and a.state in ('待出库','部分出库') ";
                    $where  .= $warehouseId ? " and b.warehouseId = '$warehouseId' " : " and b.warehouseId in ('" . implode("','", $mrpWarehouse) . "') ";
                    $result = find(array(
                        'table' => 'inventory_detail as b join inventory as a on b.orderId = a.id',
                        'where' => "a.coid = '" . self::$coid . "' and b.materialId = '$materialId'" . $where,
                        'field' => "(b.orderNum - b.num) as num,b.unit"
                    ));
                    foreach ((array)$result['array'] as $array) {
                        $num -= self::material_num_change($materialId, $array['unit'], $unit, $array['num']);
                    }
            }
        }
        return $num;
    }

    /**
     * 不小于0
     * @param $num
     * @return int|string
     */
    public function moreZero($num): int|string
    {
        if (is_numeric($num)) {
            return $num > 0 ? $num : 0;
        }
        return $num;
    }

    /**
     * 编辑页
     *
     * @return string
     */
    public function edit(): string
    {
        $id   = $this->get('id') ?: suiji();
        $info = query(self::table, " id = '" . $id . "' ");
        if ($this->get('sourceId')) {
            $qualityReason = query('quality_order_reason', "id = '" . $this->get('sourceId') . "'");
            $quality       = query('quality_order', "id = '$qualityReason[orderId]'");
        }

        $button[] = [
            "name"  => "保存为草稿",
            "type"  => "点击事件",
            "title" => "保存",
            'fun'   => "subForm('" . self::page . "EditForm','" . root . "control/adIndex.html?cla=" . self::page . "&fun=editEdit')",
        ];
        $button[] = [
            'style' => 'li-btn-blue-bg',
            "name"  => "保存并提交",
            "type"  => "点击事件",
            "title" => "保存",
            'fun'   => "subForm('" . self::page . "EditForm','" . root . "control/adIndex.html?cla=" . self::page . "&fun=saveAndCommit')"
        ];
        $tr[]     = ['', '源单编号', '产品名称', '物料编码', '物料名称', '规格型号', '单位', '额定数量', '损耗率%', '预计用量', '当前可用库存', '备注'];
        $result   = find(array('table' => self::tableMx, 'where' => "orderId = '$info[id]' order by list"));
        $totalArr = ['total_mxNum'=>0,'total_quantity'=>0,'total_inventory'=>0];
        foreach ($result['array'] as $k=>$array) {
            $material         = query('material', " id = '$array[materialId]'");
            $unitOption       = self::unitOption($material['id']);
            $machiningOrderMx = query(self::table_material, "id = '$array[targetSkuId]'");
            $inventory = self::inventory($array['materialId'], $array['unit']);
            $tr[]             = [
                componentClass::tablePperation($k+1,'delTableRow(this);calculateTotal($("#materialTable"))').
                hidden(array('name' => 'mxId[]', 'value' => $array['id'])) .
                hidden(array('name' => 'targetSkuId[]', 'value' => $array['targetSkuId'])) .
                hidden(array('name' => 'materialId[]', 'value' => $array['materialId'])),
                $machiningOrderMx['sourceNo'],
                materialClass::getName($machiningOrderMx['materialId'], short: true),
                $material['code'],
                $material['name'],
                $material['model'],
                select(array('name' => 'unitDetail[]', 'data' => ['unit' => $array['unit']], 'change' => 'unitChange', 'option' => $unitOption, 'title' => '单位', 'value' => $array['unit'])),
                text(array('name' => 'mxNum[]', 'other' => self::digitalFiltering('numPrecision'), 'blur' => 'countNum', 'value' => self::lostZero($array['num'], 'numPrecision'))),
                text(array('name' => 'loss[]', 'other' => self::digitalFiltering('lossRate'), 'class' => 'text', 'blur' => 'countNum', 'value' => $array['loss'] * 100)),
                "<span class='totalNum'>" . self::lostZero($array['quantity'], 'numPrecision') . "</span>",
                "<span class='inventory'>" . $inventory . "</span>",
                text(array('name' => 'mxText[]', 'class' => 'text textPrice', 'value' => $array['text'])),
            ];
            $totalArr['total_mxNum'] += $array['num'];
            $totalArr['total_quantity'] += $array['quantity'];
            $totalArr['total_inventory'] += $inventory;
        }
        $total      = [
            7  => "<span id='total_mxNum'>" . self::lostZero($totalArr['total_mxNum'], 'numPrecision') . "</span>",
            9  => "<span id='total_quantity'>" . self::lostZero($totalArr['total_quantity'], 'numPrecision') . "</span>",
            10 => "<span id='total_inventory'>" . self::lostZero($totalArr['total_inventory'], 'numPrecision') . "</span>",
        ];
        $list         = $this->listTable(array('tr' => $tr, 'end' => true, 'total' => $total));
        $obtainBomBtn = <<<eof
<span class="smallButton" onclick="obtainBom()">从BOM获取</span>&emsp;
<span class="smallButton" onclick="newIframe('BOM表',root+'control/adIndex.html?cla=bom&fun=edit','供应链','资料')">新建BOM</span>
eof;
        if (optionSettingClass::getSettingItem('生产', 'withOutBom')) {
            $obtainBomBtn = '';
        }
        $tableBut = <<<efo
                <span class="smallButton" onclick="layer('{$this->get('cla')}','addMx',$('form[name=machiningOrderEditForm]').serialize())">选择物料</span>
                $obtainBomBtn
efo;
        $table = <<<efo
                <div id="material-table-content">{$list}</div>
efo;

        $materialTr[] = ['', '源单类型', '源单编号', '产品编码', '产品名称', '规格型号', '单位', '计划数量', '生产批次', 'BOM版本', '工艺路线', '备注'];
        $materialTotalArr = ['material_num'=>0];
        if ($this->get('source') == 'productDemand') {
            //从产品需求
            $listIdArr = explode(',', $this->get('listId'));
            foreach ($listIdArr as $k=>$saleOrderMxId) {
                $productDemand = query('product_demands', "id = '$saleOrderMxId'");
                $saleOrderMx   = query('sale_order_detail', "id = '$productDemand[sourceMxId]'");
                $saleOrder     = query('sale_order', "id = '$productDemand[sourceId]'");
                $material      = query('material', " id = '$productDemand[materialId]'");
                $unitOption    = self::unitOption($material['id']);
                //默认bom
                $detaultBom = query('bom', "materialId = '$material[id]' and bomType like '%生产%' and state = '启用' and isDefault = 1");
                //默认工艺路线
                $routingId      = $this->defaultRouting($material['id']);
                $detaultRouting = query('routing', "id = '$routingId'");
                //已进入生产计划单的数量
                $productNum = 0;
                $result     = find(array('table' => 'machining_order_mx', 'where' => "sourceId = '$productDemand[id]'"));
                foreach ($result['array'] as $value) {
                    $productNum += self::material_num_change($value['materialId'], $value['unit'], $productDemand['unit'], $value['num']);
                }
                $materialTr[] = [
                    componentClass::tablePperation($k+1,'delTableRow(this);calculateMaterialTotal($("#materialMx"))').
                    hidden(array('name' => 'productId[]', 'value' => self::getId())) .
                    hidden(array('name' => 'sourceNo[]', 'value' => $saleOrder['orderNo'])) .
                    hidden(array('name' => 'sourceId[]', 'value' => $saleOrderMxId)) .
                    hidden(array('name' => 'skuId[]', 'value' => $saleOrderMx['materialId'])),
                    $saleOrder['orderNo'] ? '销售订单' : '',
                    $saleOrder['orderNo'] ?: '',
                    $material['code'],
                    $material['name'],
                    $material['model'],
                    select(array('name' => 'unit[]', 'data' => ['unit' => $productDemand['unit']], 'change' => 'unitChangeProduct', 'option' => $unitOption, 'title' => '单位', 'value' => $productDemand['unit'])),
                    text(array('name' => 'num[]', 'other' => self::digitalFiltering('numPrecision')." onblur=\"calculateMaterialTotal($('#materialMx'))\" ", 'value' => $this->moreZero($saleOrderMx['num'] - $productNum))),
                    text(array('name' => 'batch[]', 'value' => '')),
                    componentClass::selectText([
                        'name'      => 'bomId[]',
                        'value'     => $detaultBom['id'],
                        'title' => $detaultBom['bomVersion'],
                        'cla'       => self::page,
                        'fun'       => 'getBom',
                        'param'    => "{'materialId':'".$material['id']."'}",
                    ]),
                    componentClass::selectText([
                        'name'      => 'routingId[]',
                        'value'     => $detaultRouting['id'],
                        'title' => $detaultRouting['description'],
                        'cla'       => self::page,
                        'fun'       => 'getRouting',
                        'param'    => "{'materialId':'".$material['id']."'}",
                    ]),
                    text(array('name' => 'materialText[]', 'value' => $saleOrderMx['text'] ?: $saleOrder['text'])),
                ];
                $materialTotalArr['material_num'] += $productNum;
            }
        } else {
            $result = find(array('table' => self::table_material, 'where' => "orderId = '$info[id]' order by list"));
            foreach ($result['array'] as $k=>$array) {
                $relativeNo = $relativeType = '';
                if ($array['sourceTable'] == 'product_demands') {
                    $productDemand = query('product_demands', "id = '$array[sourceId]'");
                    $saleOrder     = query('sale_order', "id = '$productDemand[sourceId]'");
                    $relativeNo    = $saleOrder['orderNo'];
                    $relativeType  = '销售订单';
                } elseif ($array['sourceTable'] == 'sale_refund_order_detail') {
                    $saleRefundOrderMx = query('sale_refund_order_detail', "id = '$array[sourceId]'");
                    $saleRefundOrder   = query('sale_refund_order', "id = '$saleRefundOrderMx[orderId]'");
                    $relativeNo        = $saleRefundOrder['orderNo'];
                    $relativeType      = '销售退货单';
                } elseif ($array['sourceTable'] == 'sale_order_detail') {
                    $saleOrderMx  = query('sale_order_detail', "id = '$array[sourceId]'");
                    $saleOrder    = query('sale_order', "id = '$saleOrderMx[orderId]'");
                    $relativeNo   = $saleOrder['orderNo'];
                    $relativeType = '销售订单';
                } elseif ($array['sourceTable'] == 'sale_forecast_detail') {
                    $saleOrderMx  = query('sale_forecast_detail', "id = '$array[sourceId]'");
                    $saleOrder    = query('sale_forecast', "id = '$saleOrderMx[orderId]'");
                    $relativeNo   = $saleOrder['orderNo'];
                    $relativeType = '销售预测单';
                }

                $material     = query('material', " id = '$array[materialId]'");
                $unitOption   = self::unitOption($material['id']);

                $bom = query('bom', " id = '".$array['bomId']."' ");
                $routing = query('routing', " id = '".$array['routingId']."' ");
                $materialTr[] = [
                    componentClass::tablePperation($k+1,'delTableRow(this);calculateMaterialTotal($("#materialMx"))').
                    hidden(array('name' => 'productId[]', 'value' => $array['id'])) .
                    hidden(array('name' => 'sourceNo[]', 'value' => $relativeNo)) .
                    hidden(array('name' => 'sourceId[]', 'value' => $array['sourceId'])) .
                    hidden(array('name' => 'skuId[]', 'value' => $array['materialId'])),
                    $relativeType,
                    $relativeNo,
                    $material['code'],
                    $material['name'],
                    $material['model'],
                    select(array('name' => 'unit[]', 'data' => ['unit' => $array['unit']], 'change' => 'unitChangeProduct', 'option' => $unitOption, 'title' => '单位', 'value' => $array['unit'])),
                    text(array('name' => 'num[]', 'other' => self::digitalFiltering('numPrecision')." onblur=\"calculateMaterialTotal($('#materialMx'))\" ", 'value' => self::lostZero($array['num'], 'numPrecision'))),
                    text(array('name' => 'batch[]', 'value' => $array['batch'])),
                    componentClass::selectText([
                        'name'      => 'bomId[]',
                        'value'     => $bom['id'],
                        'title' => $bom['bomVersion'],
                        'cla'       => self::page,
                        'fun'       => 'getBom',
                        'param'    => "{'materialId':'".$material['id']."'}",
                    ]),
                    componentClass::selectText([
                        'name'      => 'routingId[]',
                        'value'     => $routing['id'],
                        'title' => $routing['description'],
                        'cla'       => self::page,
                        'fun'       => 'getRouting',
                        'param'    => "{'materialId':'".$material['id']."'}",
                    ]),
                    text(array('name' => 'materialText[]', 'value' => $array['text'])),
                ];
                $materialTotalArr['material_num'] += $array['num'];
            }
        }

        if (isset($qualityReason['id']) && $qualityReason['id'] && isset($quality['id'])) {
            //不良处理
            $info['lineId'] = $quality['targetId'];
            $num            = $this->get('num') ?: $qualityReason['num'];
            $material       = query('material', " id = '$quality[materialId]'");
            $unitOption     = self::unitOption($material['id']);
            //默认bom
            $detaultBom = query('bom', "materialId = '$material[id]' and bomType like '%生产%' and state = '启用' and isDefault = 1");
            //默认工艺路线
            $routingId      = $this->defaultRouting($material['id']);
            $detaultRouting = query('routing', "id = '$routingId'");
            $materialTr[]   = [
                componentClass::tablePperation(1,'delTableRow(this);calculateMaterialTotal($("#materialMx"))').
                hidden(array('name' => 'productId[]', 'value' => self::getId())) .
                hidden(array('name' => 'sourceNo[]', 'value' => $quality['orderNo'])) .
                hidden(array('name' => 'sourceId[]', 'value' => $qualityReason['id'])) .
                hidden(array('name' => 'skuId[]', 'value' => $quality['materialId'])),
                '',
                '',
                $material['code'],
                $material['name'],
                $material['model'],
                select(array('name' => 'unit[]', 'data' => ['unit' => $quality['unit']], 'change' => 'unitChangeProduct', 'option' => $unitOption, 'title' => '单位', 'value' => $quality['unit'])),
                text(array('name' => 'num[]', 'other' => self::digitalFiltering('numPrecision')." onblur=\"calculateMaterialTotal($('#materialMx'))\" ", 'value' => self::lostZero($num, 'numPrecision'))),
                text(array('name' => 'batch[]', 'value' => '')),
                componentClass::selectText([
                    'name'      => 'bomId[]',
                    'value'     => $detaultBom['id'],
                    'title' => $detaultBom['bomVersion'],
                    'cla'       => self::page,
                    'fun'       => 'getBom',
                    'param'    => "{'materialId':'".$material['id']."'}",
                ]),
                componentClass::selectText([
                    'name'      => 'routingId[]',
                    'value'     => $detaultRouting['id'],
                    'title' => $detaultRouting['description'],
                    'cla'       => self::page,
                    'fun'       => 'getRouting',
                    'param'    => "{'materialId':'".$material['id']."'}",
                ]),
                text(array('name' => 'materialText[]', 'value' => '不良返工')),
            ];
            $materialTotalArr['material_num'] += $num;
        }
        $total      = [
            7  => "<span id='material_num'>" . self::lostZero($materialTotalArr['material_num'], 'numPrecision') . "</span>",
        ];
        $materialMx = $this->listTable(array('tr' => $materialTr, 'end' => true, 'total' => $total));

        $materialMxBut = <<<efo
                    <span class="smallButton" onclick="layer('{$this->get('cla')}','addOrder',$('form[name=machiningOrderEditForm]').serialize())">关联销售单</span>
                    <span class="smallButton" onclick="layer('{$this->get('cla')}','addRefundOrder',$('form[name=machiningOrderEditForm]').serialize())">关联销售退货单</span>
                    <span class="smallButton" onclick="layer('{$this->get('cla')}','addSaleOrder',$('form[name=machiningOrderEditForm]').serialize())">关联销售预测单</span>
                    <span class="smallButton" onclick="layer('{$this->get('cla')}','addMaterial',$('form[name=machiningOrderEditForm]').serialize())">选择物料</span>
efo;

        $line = query('product_line', "id = '$info[lineId]'");

        //表单
        $data['button'] = $this->tableShowButton($button);
        $body = $this->edit4([
            '基本信息' => [
                [
                    must . "单据日期：" => text(["name" => "recordDate", 'laydate' => [], "value" => $info['recordDate'] ?: $this->date]),
                    must . "生产线："   => repeatSelect(array('name' => 'workshop', 'title' => '生产车间', 'change' => "getLine", 'sqlForm' => "product_line where coid = '" . self::$coid . "' and state = '启用'", 'sqlField' => 'workshop', 'value' => $line['workshop'])) .
                        idSelect(array('name' => 'lineId', 'sqlForm' => "product_line where coid = '" . self::$coid . "' and state = '启用' and workshop = '$line[workshop]'", 'sqlId' => 'id', 'sqlField' => 'name', 'title' => '生产线', 'value' => $info['lineId'])),
                    must . "计划开工时间：" => text(["name" => "startDate", 'laydate' => [], "value" => $info['startDate']]),
                    must . "计划完工时间：" => text(["name" => "endDate", 'laydate' => [], "value" => $info['endDate']]),
                ],
                [
                    "备注：" => text(["name" => "text", 'class' => 'long', "value" => $info['text']]),
                ],
            ]
        ]);
        $body .= $this->editList([
            'id' => 'materialMx',
            'title' => '产品明细',
            'button' => $materialMxBut,
            'html' => $materialMx,
        ]);
        $body .= $this->editList([
            'id' => 'materialTable',
            'title' => '所需物料',
            'button' => $tableBut,
            'html' => $table,
        ]);
        $data['html'] = $this->pageEdit([
            "html"  => $body,
            'hidden' => hidden(['name' => 'id', 'value' => $id]) . hidden(array('name' => 'qualityId', 'value' => $this->get('linkId'))),
        ]);
        $data['html'] .= <<<efo
<script>
//根据单位转换所需数量和库存
function unitChange(e){
    let unit = e.value;
    if (!unit){
        msg('请选择单位')
        return '';
    }
    let oldUnit = $(e).data('unit');
    $(e).data('unit',unit);
    let loss = $(e).parent().parent().find("[name='loss[]']").val();
    let num = $(e).parent().parent().find("[name='mxNum[]']").val();
    let id = $(e).parent().parent().find("[name='materialId[]']").val();
    $.post(root+'control/adIndex.html?cla={$this->get('cla')}&fun=unitChange',{unit:unit,oldUnit:oldUnit,num:num,loss:loss,id:id},function(res) {
        if (res.warn===2){
            $(e).parent().parent().find("[name='mxNum[]']").val(res.num);
            $(e).parent().parent().find(".totalNum").html(res.totalNum);
            $(e).parent().parent().find(".inventory").html(res.inventoryNum);
            calculateTotal($('#materialTable'))
        }else{
            msg(res.warn);
        }
    },'json');
}
function unitChangeProduct(e){
    let unit = e.value;
    if (!unit){
        msg('请选择单位')
        return '';
    }
    let oldUnit = $(e).data('unit');
    $(e).data('unit',unit);
    let num = $(e).parent().parent().find("[name='num[]']").val();
    let skuId = $(e).parent().parent().find("[name='skuId[]']").val();
    $.post(root+'control/adIndex.html?cla={$this->get('cla')}&fun=unitChange',{unit:unit,oldUnit:oldUnit,num:num,skuId:skuId},function(res) {
        if (res.warn===2){
            $(e).parent().parent().find("[name='num[]']").val(res.num);
        }else{
            msg(res.warn);
        }
    },'json');
}
//根据BOM计算物料使用量
function obtainBom(){
    $.post(root+'control/adIndex.html?cla={$this->get('cla')}&fun=obtainBom',$('form[name=machiningOrderEditForm]').serialize(),function(res) {
        if (res.warn ==2){
            $("#material-table-content").empty().append(res.html);
            resetNumber($("#material-table-content"))
            calculateTotal($('#materialTable'))
        }else{
            msg(res.warn);
        }
    },'json');
}
//计算计划数量
function calculateMaterialTotal(table=''){
    table = table ? table : $('.editList')
    let num = 0
    table.find("[name='num[]']").each(function(index){
        if($(this).val()){
          num+=Number($(this).val());
        }
    })
    $("#material_num").text(num.toFixed(publicSettings.numPrecision.num));
}
//计算预定数量
function countNum(e){
    let loss = $(e).parent().parent().find("[name='loss[]']").val();
    let num = $(e).parent().parent().find("[name='mxNum[]']").val();
    $(e).parent().parent().find(".totalNum").html((Number(num)*(1+Number(loss)/100)).toFixed(publicSettings.numPrecision.num))
    calculateTotal($('#materialTable'))
}
//计算总金额，总税额
function calculateTotal(table=''){
    table = table ? table : $('.editList')
    let mxNum = 0,
    totalNum = 0,
    inventory = 0;
    table.find("[name='mxNum[]']").each(function(index){
        if($(this).val()){
          mxNum+=Number($(this).val());
        }
        var totalNumText = table.find(".totalNum").eq(index).text()
        if(totalNumText){
            totalNum+=Number(totalNumText);
        }
        var inventoryText = table.find(".inventory").eq(index).text()
        if(inventoryText){
            inventory+=Number(inventoryText);
        }
    })
    $("#total_mxNum").text(mxNum.toFixed(publicSettings.numPrecision.num));
    $("#total_quantity").text(totalNum.toFixed(publicSettings.numPrecision.num));
    $("#total_inventory").text(inventory.toFixed(publicSettings.numPrecision.num));
}
//获取生产线
function getLine(e){
     let val = e.value;
     $.post(root+'control/adIndex.html?cla={$this->get('cla')}&fun=getLine',{val:val},function(res) {
             $("select[name='lineId']").html(res.html);
        },'json');
}
//选择物料
function addRow(){
    $.post(root+'control/adIndex.html?cla={$this->get('cla')}&fun=addMxEdit',$("form[name='mxForm']").serialize(),function(res) {
        $('#machiningOrderAddMx').hide();
        $("#materialTable tbody").append(res.html);
        resetNumber($("#materialTable"))
        calculateTotal($('#materialTable'))
    },'json');
}
//选择生产产品
function addMaterial(classify = ''){
    if (classify ==='saleOrder'){
         $.post(root+'control/adIndex.html?cla={$this->get('cla')}&fun=addOrderEdit',$("form[name='saleOrderForm']").serialize(),function(res) {
         if (res.warn ==2){
            $('#machiningOrderAddOrder').hide();
            $("#materialMx tbody").append(res.html);
            resetNumber($("#materialMx"))
            calculateMaterialTotal($('#materialMx'))
         }else{
             msg(res.warn);
         }
        },'json');
    }else if(classify ==='saleRefundOrder'){
        $.post(root+'control/adIndex.html?cla={$this->get('cla')}&fun=addRefundOrderEdit',$("form[name='saleRefundOrderForm']").serialize(),function(res) {
         if (res.warn ==2){
            $('#machiningOrderAddRefundOrder').hide()
           $("#materialMx tbody").append(res.html);
           resetNumber($("#materialMx"))
           calculateMaterialTotal($('#materialMx'))
         }else{
             msg(res.warn);
         }
        },'json');
    }else if(classify ==='saleForecast'){
        $.post(root+'control/adIndex.html?cla={$this->get('cla')}&fun=addSaleOrderEdit',$("form[name='saleForecastForm']").serialize(),function(res) {
         if (res.warn ==2){
           $('#machiningOrderAddSaleOrder').hide()
           $("#materialMx tbody").append(res.html);
           resetNumber($("#materialMx"))
           calculateMaterialTotal($('#materialMx'))
         }else{
             msg(res.warn);
         }
        },'json');
    }else{
         $.post(root+'control/adIndex.html?cla={$this->get('cla')}&fun=addMaterialEdit',$("form[name='materialMxForm']").serialize(),function(res) {
         if (res.warn ==2){
             $('#machiningOrderAddMaterial').hide()
              $("#materialMx tbody").append(res.html);
              resetNumber($("#materialMx"))
              calculateMaterialTotal($('#materialMx'))
         }else{
             msg(res.warn);
         }
        },'json');
    }
    
}

</script>

efo;
        //返回
        return $this->tab($data);
    }

    /**
     * 模糊查询BOM
     *
     * @return bool|string
     */
    public function getBom(): bool|string
    {
        $val          = $this->post('keyword');//关键词
        $json['warn'] = 0;
        $json['data'] = [];
        $where        = empty($val) ? "1=1" : "(bomVersion like '%" . $val . "%')";
        $where        .= " and coid = '" . self::$coid . "' and materialId = '".$this->post('materialId')."' and bomType like '%生产%' and state = '启用' ";
        $all          = find(['table' => 'bom', 'where' => $where . " order by time desc limit 100"]);
        foreach ($all['array'] as $val) {
            $json['data'][] = [
                'title'   => $val['bomVersion'],
                'orderNo' => $val['orderNo'],
                'id'      => $val['id'],
            ];
        }
        $json['warn'] = 2;
        return json_encode($json, 320);
    }

    /**
     * 模糊查询工艺路线
     *
     * @return bool|string
     */
    public function getRouting(): bool|string
    {
        $material = query("material"," id = '".$this->post('materialId')."' ");
        $val          = $this->post('keyword');//关键词
        $json['warn'] = 0;
        $json['data'] = [];
        $where        = empty($val) ? "1=1" : "(description like '%" . $val . "%')";
        $where        .= " and coid = '" . self::$coid . "' and type = 1 and state = '启用' and (materialId like '%{$material['id']}%' or (materialTypeId in ('" . implode("','", explode('/',$material['typePath'])) . "') and materialId = ''))  ";
        $all          = find(['table' => 'routing', 'where' => $where . " order by time desc limit 100"]);
        foreach ($all['array'] as $val) {
            $json['data'][] = [
                'title'   => $val['description'],
                'orderNo' => $val['orderNo'],
                'id'      => $val['id'],
            ];
        }
        $json['warn'] = 2;
        return json_encode($json, 320);
    }


    /**
     * 获取 物料单位选项
     *
     * @param $materialId
     * @return array
     */
    public static function unitOption($materialId): array
    {
        $material     = query('material', " id = '$materialId'");
        $unitOption[] = $material['unit'];
        if ($material['unit_one']) {
            $unitOption[] = $material['unit_one'];
        }
        if ($material['unit_two']) {
            $unitOption[] = $material['unit_two'];
        }
        return array_filter($unitOption);
    }

    /**
     * 根据单位显示所需数量
     */
    public function unitChange()
    {
        $materialId = $this->post('skuId');
        $num           = $this->post('num');
        $loss          = (float)$this->post('loss') / 100;
        $unit          = $this->post('unit');
        $oldUnit       = $this->post('oldUnit');
        if (empty($materialId)) {
            $json['warn'] = '未找到物料';
        } elseif (empty($unit)) {
            $json['warn'] = '未找到单位';
        } else {
            $json['warn']         = 2;
            $inventoryNum         = self::inventory($materialId, $unit);
            $json['num']          = $num;
            $json['totalNum']     = self::formattingFigure(floatval($num) * (1 + (floatval($loss))), 'numPrecision');
            $json['inventoryNum'] = $inventoryNum;
            if ($oldUnit) {
                $num              = self::material_num_change($materialId, $oldUnit, $unit, $num);
                $json['totalNum'] = self::formattingFigure(floatval($num) * (1 + (floatval($loss))), 'numPrecision');
                $json['num']      = $num;
            }
        }

        return json_encode($json, 320);
    }

    /**
     * 物料单位转换
     *
     * @param     $materialId
     * @param     $from
     * @param     $to
     * @param int $num
     * @return int|float
     */
    public static function material_num_change($materialId, $from, $to, $num = 1): int|float
    {
        $num = $num ?: 0;
        if ($from == $to) {
            return $num;
        }
        $material               = query('material', " id = '$materialId'","id,unit_one,unit_one_num,unit_two,unit_two_num");
        if(empty($material['id'])){
            return 0;
        }
        if ($from && $from == $material['unit_one']) {
            $num = $material['unit_one_num'] * $num;
        } elseif ($from && $from == $material['unit_two']) {
            $num = $material['unit_two_num'] * $num;
        }
        if ($to && $to == $material['unit_one']) {
            $num = $material['unit_one_num'] > 0 ? ($num / $material['unit_one_num']) : 0;
        } elseif ($to && $to == $material['unit_two']) {
            $num = $material['unit_two_num'] > 0 ? ($num / $material['unit_two_num']) : 0;
        }
        return self::formattingFigure($num, 'numPrecision');
    }

    /**
     * Desc : 物料单位转换,sql组合
     * User : you name
     * Date : 2024-04-24 11:22
     * @param $numField
     * @param $fromUnitFiled
     * @param $materialAlias
     * @param $toUnit
     * @return string
     */
    public static function material_num_change_sql($numField, $fromUnitFiled, $materialAlias, $toUnit): string
    {
        if(!$numField || !$materialAlias){
            return '';
        }
        return  "
        (CASE WHEN {$fromUnitFiled} = {$materialAlias}.unit THEN {$numField} WHEN {$fromUnitFiled} = {$materialAlias}.unit_one THEN {$numField} / {$materialAlias}.unit_one_num WHEN {$fromUnitFiled} = {$materialAlias}.unit_two THEN {$numField} / {$materialAlias}.unit_two_num ELSE 0 END
        * 
        CASE WHEN '$toUnit' = {$materialAlias}.unit THEN 1 WHEN '$toUnit' = {$materialAlias}.unit_one THEN {$materialAlias}.unit_one_num WHEN '$toUnit' = {$materialAlias}.unit_two THEN {$materialAlias}.unit_two_num ELSE 0 END) as num
        ";
    }

    /**
     * 根据BOM计算物料使用量
     */
    public function obtainBom(): bool|string
    {
        $productIdArr     = $this->post('productId');
        $sourceNoArr      = $this->post('sourceNo');
        $materialIdArr = $this->post('skuId');
        $bomIdArr         = $this->post('bomId') ?: [];
        $numArr           = $this->post('num');
        $unitArr          = $this->post('unit');
        if (!$this->power(self::page, "edit")) {
            $json['warn'] = "你没有编辑" . self::title . "的权限";
        } elseif (static::is_null_array($materialIdArr) || static::is_null_array($productIdArr)) {
            $json['warn'] = '请选择产品';
        } elseif (static::is_null_array($numArr)) {
            $json['warn'] = '您还有物料未填写生产数量';
        } elseif (static::is_null_array($bomIdArr)) {
            $json['warn'] = '您还有物料未选择BOM版本';
        } elseif (static::is_null_array($unitArr)) {
            $json['warn'] = '您还有物料未选择单位';
        } else {
            $tr[] = ['', '源单编号', '产品名称', '物料编码', '物料名称', '规格型号', '单位', '额定数量', '损耗率%', '预计用量', '当前可用库存', '备注'];
            foreach ($materialIdArr as $k => $materialId) {
                $bomId         = $bomIdArr[$k];
                $num           = $numArr[$k];
                $unit          = $unitArr[$k];
                $material_list = self::bomMx($bomId, $materialId, $num, $unit);
                $material_list = $this->merge_virtual($material_list, []);

                $material    = query('material', "id = '$materialId'");
                if (static::is_null_array($material_list)) {
                    $this->logText(array('type' => self::page, 'text' => "物料【{$material['code']}-{$material['name']}-{$material['model']}】未在BOM中找到所需物料"));
                } else {
                    foreach ($material_list as $key=>$array) {
                        $material    = query('material', " id = '$array[materialId]'");
                        $unitOption  = self::unitOption($material['id']);
                        $num         = self::formattingFigure($array['quantity'], 'numPrecision');
                        $loss        = self::formattingFigure($array['num'] > 0 ? (($array['quantity'] - $array['num']) / $array['num'] * 100) : 0, 'lossRate');
                        $tr[]        = [
                            componentClass::tablePperation($key+1,'delTableRow(this);calculateTotal($("#materialTable"))').
                            hidden(array('name' => 'mxId[]', 'value' => self::getId())) .
                            hidden(array('name' => 'targetSkuId[]', 'value' => $productIdArr[$k])) .
                            hidden(array('name' => 'materialId[]', 'value' => $array['materialId'])),
                            $sourceNoArr[$k],
                            materialClass::getName($materialId, short: true),
                            $material['code'],
                            $material['name'],
                            $material['model'],
                            select(array('name' => 'unitDetail[]', 'change' => 'unitChange', 'data' => ['unit' => $material['unit']], 'option' => $unitOption, 'title' => '单位', 'value' => $material['unit'])),
                            text(array('name' => 'mxNum[]', 'other' => self::digitalFiltering('numPrecision'), 'blur' => 'countNum', 'value' => self::formattingFigure($array['num'], 'numPrecision'))),
                            text(array('name' => 'loss[]', 'other' => self::digitalFiltering('lossRate'), 'class' => 'text', 'blur' => 'countNum', 'value' => $loss)),
                            "<span class='totalNum'>{$num}</span>",
                            "<span class='inventory'>" . self::inventory($array['materialId'], $material['unit']) . "</span>",
                            text(array('name' => 'mxText[]', 'class' => 'text textPrice')),
                        ];
                    }
                }
            }
            $json['warn'] = 2;
            $json['html'] = $this->tableMany(array('tr' => $tr, 'end' => true));
        }
        return json_encode($json, 320);
    }

    /**
     * 写入日志
     *
     * @param $data
     * @return string
     */
    public static function log($data): string
    {
        if (empty($data['text'])) {
            test("【" . $data['type'] . "】日志插入失败，原因：日志说明为空");
            return '';
        } else {
            $data['coid'] = self::$coid;
            if (empty($data['stid'])) {
                $data['stid'] = self::$stid;
            }
            $data['time'] = date("Y-m-d H:i:s");
            return insert(["table" => "log", "field" => $data]);
        }
    }


    /**
     * 有虚拟件时需要合并相同物料
     * @param $oldArr
     * @param $newArr
     * @return array
     */
    public function merge_virtual($oldArr, $newArr): array
    {
        $allArr = array_merge($oldArr, $newArr);
        $temp   = array();
        foreach ($allArr as $item) {
            list($materialId, $quantity, $num) = array_values($item);
            if (array_key_exists($materialId, $temp)) {
                $temp[$materialId]['quantity'] += $quantity;
                $temp[$materialId]['num']      += $num;
            } else {
                $temp[$materialId]['quantity'] = $quantity;
                $temp[$materialId]['num']      = $num;
            }
        }
        $result = [];
        foreach ($temp as $key => $array) {
            $result[] = array('materialId' => $key, 'quantity' => $array['quantity'], 'num' => $array['num']);
        }
        return $result;
    }

    /**
     * 组合相同的所需物料
     *
     * @param $oldArr
     * @param $newArr
     * @return array
     */
    public function merge_material($oldArr, $newArr): array
    {
        if (static::is_null_array($oldArr)) {
            return $newArr;
        } else {
            $allArr = array_merge($oldArr, $newArr);
            $temp   = array();
            foreach ($allArr as $item) {
                list($p, $n) = array_values($item);
                $temp[$p] = array_key_exists($p, $temp) ? $temp[$p] + $n : $n;
            }
            $result = [];
            foreach ($temp as $key => $num) {
                $result[] = array('materialId' => $key, 'num' => $num);
            }
            return $result;
        }
    }

    /**
     * 需要计算生产周期
     * @param $oldArr
     * @param $newArr
     * @return array
     */
    public function merge_material_production($oldArr, $newArr)
    {
        if (static::is_null_array($oldArr)) {
            return $newArr;
        } else {
            $allArr     = array_merge($oldArr, $newArr);
            $temp       = array();
            $production = [];//仓库数组
            foreach ($allArr as $item) {
                list($p, $n, $w) = array_values($item);
                $temp[$p]       = array_key_exists($p, $temp) ? $temp[$p] + $n : $n;
                $production[$p] = array_key_exists($p, $production) ? ($production[$p] > $w ? $production[$p] : $w) : $w;
            }
            $result = [];
            foreach ($temp as $key => $num) {
                $result[] = array('materialId' => $key, 'num' => $num, 'production' => $production[$key]);
            }
            return $result;
        }
    }

    /**
     * 组合相同的所需物料
     * @param $oldArr
     * @param $newArr
     * @return array
     */
    public function merge_material_two($oldArr, $newArr): array
    {
        if (static::is_null_array($oldArr)) {
            return $newArr;
        } else {
            $allArr        = array_merge($oldArr, $newArr);
            $temp          = array();
            $warehouseTemp = [];//仓库数组
            foreach ($allArr as $item) {
                list($p, $n, $w) = array_values($item);
                $temp[$p]          = array_key_exists($p, $temp) ? $temp[$p] + $n : $n;
                $warehouseTemp[$p] = $w;
            }
            $result = [];
            foreach ($temp as $key => $num) {
                $result[] = array('materialId' => $key, 'num' => $num, 'warehouseId' => $warehouseTemp[$key]);
            }
            return $result;
        }
    }

    /**
     * 保存并提交
     * @return bool|string
     */
    public function saveAndCommit(): bool|string
    {
        $resultJson = $this->editEdit();
        $result     = json_decode($resultJson, true);
        if ($result['warn'] == 2) {
            unset($_SESSION['warn']);
            $resultJson = $this->commitEdit();
        }
        return $resultJson;
    }

    /**
     * 编辑
     *
     * @return bool|string
     */
    public function editEdit(): bool|string
    {
        $id               = $this->post('id') ?: self::getId();
        $mxIdArr          = $this->post('mxId') ?: [];
        $materialIdArr = $this->post('materialId');
        $numArr           = $this->post('mxNum');
        $unitArr          = $this->post('unitDetail');
        $mxTextArr        = $this->post('mxText');
        $lossArr          = $this->post('loss');
        $targetSkuIdArr   = $this->post('targetSkuId');

        $productIdArr    = $this->post('productId') ?: [];
        $sourceIdArr     = $this->post('sourceId');
        $sourceNoArr     = $this->post('sourceNo');
        $skuIdArr        = $this->post('skuId');
        $numMxArr        = $this->post('num');
        $batchArr        = $this->post('batch');
        $unitMxArr       = $this->post('unit');
        $materialTextArr = $this->post('materialText');
        $bomIdArr        = $this->post('bomId');
        $routingIdArr    = $this->post('routingId');
        $field           = [
            'type'       => '生产加工',
            'text'       => $this->post('text'),
            'lineId'     => $this->post('lineId'),
            'recordDate' => $this->post('recordDate'),
            'startDate'  => $this->post('startDate'),
            'endDate'    => $this->post('endDate'),
        ];
        $where           = " id = '$id' ";
        $info            = query(self::table, $where);
        //判断
        if (!$this->power(self::page, "edit")) {
            $json['warn'] = "你没有编辑" . self::title . "的权限";
        } elseif (!empty($info['id']) && $info['state'] == '已确认') {
            $json['warn'] = '此生产计划已确认，不能修改';
        }elseif(self::is_null_array($productIdArr)){
            $json['warn'] = '请选择产品明细';
        } else {
            $this->startTrans();
            if (!$info['id']) {
                $userNo       = codeTemplateClass::generateCode(self::page, [
                    'prefix' => 'SCJH' . date('Ymd', strtotime($field['recordDate'])),
                    'table'  => self::table,
                    'field'  => 'orderNo',
                    'where'  => "and coid = '" . self::$coid . "'",
                ], $field);
                $field        = array_merge($field, [
                    'id'        => $id,
                    'orderNo'   => $userNo,
                    'orderType' => '生产计划',
                    'state'     => '草稿',
                    'coid'      => self::$coid,
                    'stid'      => $this->control['stid'],
                ]);
                $json['warn'] = static::insert(["table" => self::table, "field" => $field]);
            } else {
                $json['warn'] = static::update([
                    "table" => self::table,
                    "field" => $field,
                    "where" => $where,
                ]);
            }

            //添加或更新明细
            foreach ($mxIdArr as $k => $nid) {
                $mx          = query(self::tableMx, "id = '$nid'");
                $lossArr[$k] = $lossArr[$k] ?: 0;
                $numArr[$k]  = self::formattingFigure($numArr[$k], 'numPrecision');
                if (!empty($mx['id'])) {
                    //更新
                    $warn = static::update([
                        "table" => self::tableMx,
                        "field" => [
                            'materialId'    => $materialIdArr[$k],
                            'num'           => $numArr[$k],
                            'unit'          => $unitArr[$k],
                            'quantity'      => self::formattingFigure($numArr[$k] * (1 + $lossArr[$k] / 100), 'numPrecision'),
                            'loss'          => floatval($lossArr[$k]) / 100,
                            'text'          => $mxTextArr[$k],
                            'targetSkuId'   => $targetSkuIdArr[$k],
                        ],
                        "where" => " id = '$nid'",
                    ]);
                } else {
                    //新增
                    $warn = static::insert([
                        "table" => self::tableMx,
                        "field" => [
                            'id'            => $nid,
                            'orderId'       => $id,
                            'coid'          => self::$coid,
                            'materialId'    => $materialIdArr[$k],
                            'num'           => $numArr[$k],
                            'unit'          => $unitArr[$k],
                            'quantity'      => self::formattingFigure($numArr[$k] * (1 + $lossArr[$k] / 100), 'numPrecision'),
                            'loss'          => floatval($lossArr[$k]) / 100,
                            'text'          => $mxTextArr[$k],
                            'targetSkuId'   => $targetSkuIdArr[$k],
                        ],
                    ]);
                }
                if (!in_array($warn, ['新增成功', '更新成功'])) {
                    $json['warn'] = $warn;
                    break;
                }
            }

            //添加或更新生产产品明细
            $noBomSign = false;
            foreach ($productIdArr as $k => $mxId) {
                $mx          = query(self::table_material, "id = '$mxId'");
                $sourceTable = '';

                if (static::total(array('table' => 'product_demands', 'where' => "id = '" . $sourceIdArr[$k] . "'")) > 0) {
                    $sourceTable = 'product_demands';
                } elseif (static::total(array('table' => 'quality_order_reason', 'where' => "id = '" . $sourceIdArr[$k] . "'")) > 0) {
                    $sourceTable = 'quality_order_reason';
                } elseif (static::total(array('table' => 'sale_refund_order_detail', 'where' => "id = '" . $sourceIdArr[$k] . "'")) > 0) {
                    $sourceTable = 'sale_refund_order_detail';
                } elseif (static::total(array('table' => 'sale_order_detail', 'where' => "id = '" . $sourceIdArr[$k] . "'")) > 0) {
                    $sourceTable = 'sale_order_detail';
                } elseif (static::total(array('table' => 'sale_forecast_detail', 'where' => "id = '" . $sourceIdArr[$k] . "'")) > 0) {
                    $sourceTable = 'sale_forecast_detail';
                }
                if (!$bomIdArr[$k]) {
                    $noBomSign = true;
                }
                if (!empty($mx['id'])) {
                    //更新
                    $warn = static::update([
                        "table" => self::table_material,
                        "field" => [
                            'materialId'    => $skuIdArr[$k],
                            'sourceId'      => $sourceIdArr[$k],
                            'sourceNo'      => $sourceNoArr[$k],
                            'sourceTable'   => $sourceTable,
                            'batch'         => $batchArr[$k],
                            'num'           => $numMxArr[$k],
                            'unit'          => $unitMxArr[$k],
                            'bomId'         => $bomIdArr[$k],
                            'routingId'     => $routingIdArr[$k],
                            'text'          => $materialTextArr[$k],
                        ],
                        "where" => " id = '$mxId'",
                    ]);
                } else {
                    //新增
                    $warn = static::insert([
                        "table" => self::table_material,
                        "field" => [
                            'id'            => $mxId,
                            'orderId'       => $id,
                            'coid'          => self::$coid,
                            'sourceId'      => $sourceIdArr[$k],
                            'sourceNo'      => $sourceNoArr[$k],
                            'sourceTable'   => $sourceTable,
                            'materialId'    => $skuIdArr[$k],
                            'num'           => $numMxArr[$k],
                            'batch'         => $batchArr[$k],
                            'unit'          => $unitMxArr[$k],
                            'bomId'         => $bomIdArr[$k],
                            'routingId'     => $routingIdArr[$k],
                            'text'          => $materialTextArr[$k],
                        ],
                    ]);
                }
                if (!in_array($warn, ['新增成功', '更新成功'])) {
                    $json['warn'] = $warn;
                    break;
                }
            }
            if (in_array($json['warn'], ["新增成功", "更新成功"])) {
                $this->commitTrans();
                delete(array('table' => self::tableMx, 'where' => " id not in ('" . implode("','", $mxIdArr) . "') and orderId = '$id'"));
                delete(array('table' => self::table_material, 'where' => " id not in ('" . implode("','", $productIdArr) . "') and orderId = '$id'"));
                if ($json['warn'] == '新增成功' && $this->post('qualityId')) {
                    //更新不良处理状态
                    $linkId = explode(',', $this->post('qualityId'));
                    if (is_array($linkId)) {
                        update(array(
                            'table' => 'quality_order_reason',
                            'field' => array('state' => '已处理', 'dealDate' => $this->date, 'dealId' => self::$stid),
                            'where' => "id in ('" . implode("','", $linkId) . "') and state = '待处理' and coid = '" . self::$coid . "'"
                        ));
                    }
                }
                if ($json['warn'] == '新增成功') {
                    self::insertOperateLog($id,self::title,"新建");
                }
                //添加日志
                $type = mb_substr($json['warn'], 0, 2, 'utf-8');
                $text = $this->control['name'] . $type . "了" . self::title . " id：【" . $id . '】';
                $this->logText(["type" => self::page, "text" => $text]);
                //返回
                $_SESSION['warn'] = $noBomSign ? $json['warn'] . '，存在未选择BOM的产品明细' : $json['warn'];
                $json['warn']     = 2;
                $json['href']     = root . "control/adIndex.html?cla=" . $this->get('cla') . "&fun=detail&id=" . $id;
            } else {
                $this->rollback();
            }
        }

        resp:
        return json_encode($json, 320);
    }

    /**
     * 获取生产线
     */
    public function getLine(): bool|string
    {
        $workshop     = $this->post('val');
        $json['html'] = idSelect(array(
            'name' => 'lineId',
            'sqlForm' => "product_line where coid = '" . self::$coid . "' and state = '启用' and workshop = '$workshop'",
            'sqlId' => 'id',
            'sqlField' => 'name',
            'title' => '生产线',
            'type' => 'option'
        ));
        return json_encode($json, 320);
    }

    public function addSaleOrder()
    {
        $html   =
            text(array("name" => "orderNo", "title" => "销售预测单号")) .
            text(array("name" => "code", "title" => "物料编码")) .
            text(array("name" => "materialName", "title" => "物料名称")) .
            text(array("name" => "model", "title" => "规格型号")) .
            hidden(array("name" => "id", "value" => $this->post('id'))) .
            hidden(array("name" => "soruceId", "value" => $this->post('sourceId') ? implode(',', $this->post('sourceId')) : ''));
        $button = "<input onclick=\"addMaterial('saleForecast')\" type='button' class='searchSub m-l-5' value='提交'>";
        $html   = $this->search($html);
        return $this->layer(array("title" => "添加", "height" => 600, "width" => 1000, "html" => $html, "button" => $button));
    }

    /**
     * 从销售预测单中选择
     *
     * @return bool|string
     */
    public function addSaleOrderSearch(): bool|string
    {
//        $sourceId = $this->post('soruceId') ? explode(',', $this->post('soruceId')) : [];
        $id    = $this->post('id');
        $where = " a.coid='" . self::$coid . "' ";//and a.id not in ('" . implode("','", $sourceId) . "')
        $and   = " and b.isClose = 0";
        $like  = [
            'b.orderNo' => $this->post('orderNo'),
            'c.name'    => $this->post('materialName'),
            'c.code'    => $this->post('code'),
            'c.model'   => $this->post('model'),
        ];
        $equal = [
            'b.state' => '已确认',
        ];

        $where .= search(["like" => $like, "=" => $equal]) . $and;
        $page  = $this->page([
            "table"   => "sale_forecast_detail as a join sale_forecast as b on a.orderId = b.id join material as c on c.id = a.materialId",
            'field'   => "a.*,c.name,c.code,c.model,b.orderNo",
            "where"   => $where,
            'pageNum' => 50,
            "order"   => "b.recordDate desc,a.list asc",
        ]);
        //获取表头
        $checkAll = $this->checkAll("saleForecastForm", "mxId");
        $tr[]     = [$checkAll, '销售预测单号', '物料编码', '物料名称', '规格型号', '单位', '数量', '已生产数量'];
        //获取记录
        foreach ($page['array'] as $array) {
            //已生产数量（只要是在生产计划中就算）
            $machiningNum = 0;
            $all          = find(array('table' => self::table_material, 'where' => "coid = '" . self::$coid . "' and materialId = '$array[materialId]' and orderId !='$id' and sourceId = '$array[id]'"));
            foreach ($all['array'] as $val) {
                $machiningNum += self::material_num_change($array['materialId'], $val['unit'], $array['unit'], $val['num']);
            }
            $dominate = array(
                "box" => array("name" => "mxId", "value" => $array['id']),
            );
            $tr[]     = array(
                $this->dominate($dominate),
                $array['orderNo'],
                $array['code'],
                $array['name'],
                $array['model'],
                $array['unit'],
                self::lostZero($array['num'], 'numPrecision'),
                $machiningNum,
            );
        }
        $json['html'] = $page['hint'] .
            "<form name='saleForecastForm'>" .
                hidden(array('name' => 'id', 'value' => $this->post('id'))) .
                $this->tableMany(array("tr" => $tr, "showSort" => true, "page" => $page['para'])) .
            "</form>" .
            $page['button'];
        return json_encode($json, 320);
    }

    /**
     * 从销售预测单中选择
     */
    public function addSaleOrderEdit(): bool|string
    {
        $id      = $this->post('id');
        $mxIdArr = $this->post('mxId');
        if (!$this->power(self::page, 'edit')) {
            $json['warn'] = '你还没有编辑权限';
        } elseif (static::is_null_array($mxIdArr)) {
            $json['warn'] = '请选择销售单产品';
        } else {
            $materialTr = [];
            foreach ($mxIdArr as $k=>$mxId) {
                $saleOrderMx = query('sale_forecast_detail', "id = '$mxId'");
                $saleOrder   = query('sale_forecast', "id = '$saleOrderMx[orderId]'");
                $material    = query('material', "id = '$saleOrderMx[materialId]'");
                $unitOption  = self::unitOption($material['id']);
                //已在生产单中的数量
                $machiningNum = 0;
                $all          = find(array('table' => self::table_material, 'where' => "coid = '" . self::$coid . "' and id !='$id' and sourceId = '$saleOrder[id]' and materialId = '$material[id]'"));
                foreach ($all['array'] as $val) {
                    $machiningNum += self::material_num_change($material['id'], $val['unit'], $saleOrderMx['unit'], $val['num']);
                }
                $num = ($saleOrderMx['num'] - $machiningNum) < 0 ? 0 : self::formattingFigure($saleOrderMx['num'] - $machiningNum, 'numPrecision');
                //默认bom
                $detaultBom = query('bom', "materialId = '$material[id]' and bomType like '%生产%' and state = '启用' and isDefault = 1");
                //默认工艺路线
                $routingId      = $this->defaultRouting($material['id']);
                $detaultRouting = query('routing', "id = '$routingId'");

                $id           = self::getId();
                $unit         = select(array('name' => 'unit[]', 'change' => 'unitChangeProduct', 'data' => ['unit' => $material['unit']], 'option' => $unitOption, 'value' => $saleOrderMx['unit'], 'title' => '单位'));
                $bom          = componentClass::selectText([
                    'name'      => 'bomId[]',
                    'value'     => $detaultBom['id'],
                    'title' => $detaultBom['bomVersion'],
                    'cla'       => self::page,
                    'fun'       => 'getBom',
                    'param'    => "{'materialId':'".$material['id']."'}",
                ]);
                $routing      = componentClass::selectText([
                    'name'      => 'routingId[]',
                    'value'     => $detaultRouting['id'],
                    'title' => $detaultRouting['description'],
                    'cla'       => self::page,
                    'fun'       => 'getRouting',
                    'param'    => "{'materialId':'".$material['id']."'}",
                ]);
                $materialTr[] = [
                    componentClass::tablePperation($k+1,'delTableRow(this);calculateMaterialTotal($("#materialMx"))').
                    hidden(array('name' => 'productId[]', 'value' => self::getId())) .
                    hidden(array('name' => 'sourceNo[]', 'value' => $saleOrder['orderNo'])) .
                    hidden(array('name' => 'sourceId[]', 'value' => $mxId)) .
                    hidden(array('name' => 'skuId[]', 'value' => $material['id'])),
                    '销售预测单',
                    $saleOrder['orderNo'],
                    $material['code'],
                    $material['name'],
                    $material['model'],
                    $unit,
                    text(array('name' => 'num[]', 'value' => $num, 'other' => self::digitalFiltering('numPrecision')." onblur=\"calculateMaterialTotal($('#materialMx'))\" ")),
                    text(array('name' => 'batch[]')),
                    $bom,
                    $routing,
                    text(array('name' => 'materialText[]')),
                ];
            }
            $json['warn'] = 2;
            $json['html'] = $this->tableMany(['tr' => $materialTr, 'thead' => false]);
        }
        return json_encode($json, 320);
    }

    /**
     * 从销售单中选择
     *
     * @return bool|string
     */
    public function addOrder(): bool|string
    {
        $html   =
            text(array("name" => "orderNo", "title" => "销售单号")) .
            text(array("name" => "code", "title" => "物料编码")) .
            text(array("name" => "materialName", "title" => "物料名称")) .
            text(array("name" => "model", "title" => "规格型号")) .
            hidden(array("name" => "id", "value" => $this->post('id'))) .
            hidden(array("name" => "soruceId", "value" => $this->post('sourceId') ? implode(',', $this->post('sourceId')) : ''));
        $button = "<input onclick=\"addMaterial('saleOrder')\" type='button' class='searchSub m-l-5' value='提交'>";
        $html   = $this->search($html);
        return $this->layer(array("title" => "添加", "height" => 600, "width" => 1000, "html" => $html, "button" => $button));
    }

    /**
     * 从销售单中选择
     *
     * @return bool|string
     */
    public function addOrderSearch(): bool|string
    {
//        $sourceId = $this->post('soruceId') ? explode(',', $this->post('soruceId')) : [];
        $id    = $this->post('id');
        $where = " a.coid='" . self::$coid . "'";// and a.id not in ('" . implode("','", $sourceId) . "')
        $and   = " and b.isClose = 0";
        $like  = [
            'b.orderNo' => $this->post('orderNo'),
            'c.name'    => $this->post('materialName'),
            'c.code'    => $this->post('code'),
            'c.model'   => $this->post('model'),
        ];
        $equal = [
            'b.state' => '已确认',
        ];

        $where .= search(["like" => $like, "=" => $equal]) . $and;
        $page  = $this->page([
            "table"   => "sale_order_detail as a join sale_order as b on a.orderId = b.id join material as c on c.id = a.materialId",
            'field'   => "a.*,c.name,c.code,c.model,b.orderNo",
            "where"   => $where,
            'pageNum' => 50,
            "order"   => "b.recordDate desc,a.list asc",
        ]);
        //获取表头
        $checkAll = $this->checkAll("saleOrderForm", "mxId");
        $tr[]     = [$checkAll, '销售订单号', '物料编码', '物料名称', '规格型号', '单位', '销售数量', '已生产数量'];
        //获取记录
        foreach ($page['array'] as $array) {
            //已生产数量（只要是在生产计划中就算）
            $machiningNum = 0;
            $all          = find(array('table' => self::table_material, 'where' => "coid = '" . self::$coid . "' and materialId = '$array[materialId]' and orderId !='$id' and sourceId = '$array[id]'"));
            foreach ($all['array'] as $val) {
                $machiningNum += self::material_num_change($array['materialId'], $val['unit'], $array['unit'], $val['num']);
            }
            $dominate = array(
                "box" => array("name" => "mxId", "value" => $array['id']),
            );
            $tr[]     = array(
                $this->dominate($dominate),
                $array['orderNo'],
                $array['code'],
                $array['name'],
                $array['model'],
                $array['unit'],
                self::lostZero($array['num'], 'numPrecision'),
                $machiningNum,
            );
        }
        $json['html'] = $page['hint'] .
            "<form name='saleOrderForm'>" .
                hidden(array('name' => 'id', 'value' => $this->post('id'))) .
                $this->tableMany(array("tr" => $tr, "showSort" => true, "page" => $page['para'])) .
            "</form>" .
            $page['button'];
        return json_encode($json, 320);
    }

    /**
     * 从销售单中选择
     */
    public function addOrderEdit(): bool|string
    {
        $id      = $this->post('id');
        $mxIdArr = $this->post('mxId');
        if (!$this->power(self::page, 'edit')) {
            $json['warn'] = '你还没有编辑权限';
        } elseif (static::is_null_array($mxIdArr)) {
            $json['warn'] = '请选择销售单产品';
        } else {
            $materialTr = [];
            foreach ($mxIdArr as $mxId) {
                $saleOrderMx = query('sale_order_detail', "id = '$mxId'");
                $saleOrder   = query('sale_order', "id = '$saleOrderMx[orderId]'");
                $material    = query('material', "id = '$saleOrderMx[materialId]'");
                $unitOption  = self::unitOption($material['id']);
                //已在生产单中的数量
                $machiningNum = 0;
                $all          = find(array(
                    'table' => self::table_material,
                    'where' => "coid = '" . self::$coid . "' and id !='$id' and sourceId = '$saleOrder[id]' and materialId = '$material[id]'"
                ));
                foreach ($all['array'] as $val) {
                    $machiningNum += self::material_num_change($material['id'], $val['unit'], $material['unit'], $val['num']);
                }
                $num = ($saleOrderMx['num'] - $machiningNum) < 0 ? 0 : self::formattingFigure($saleOrderMx['num'] - $machiningNum, 'numPrecision');
                //默认bom
                $detaultBom = query('bom', "materialId = '$material[id]' and bomType like '%生产%' and state = '启用' and isDefault = 1");
                //默认工艺路线
                $routingId      = $this->defaultRouting($material['id']);
                $detaultRouting = query('routing', "id = '$routingId'");

                $id           = self::getId();
                $unit         = select(array('name' => 'unit[]', 'change' => 'unitChangeProduct', 'data' => ['unit' => $material['unit']], 'option' => $unitOption, 'value' => $material['unit'], 'title' => '单位'));
                $bom          = componentClass::selectText([
                    'name'      => 'bomId[]',
                    'value'     => $detaultBom['id'],
                    'title' => $detaultBom['bomVersion'],
                    'cla'       => self::page,
                    'fun'       => 'getBom',
                    'param'    => "{'materialId':'".$material['id']."'}",
                ]);
                $routing      = componentClass::selectText([
                    'name'      => 'routingId[]',
                    'value'     => $detaultRouting['id'],
                    'title' => $detaultRouting['description'],
                    'cla'       => self::page,
                    'fun'       => 'getRouting',
                    'param'    => "{'materialId':'".$material['id']."'}",
                ]);
                $materialTr[] = [
                    componentClass::tablePperation('','delTableRow(this);calculateMaterialTotal($("#materialMx"))').
                    hidden(array('name' => 'productId[]', 'value' => self::getId())) .
                    hidden(array('name' => 'sourceNo[]', 'value' => $saleOrder['orderNo'])) .
                    hidden(array('name' => 'sourceId[]', 'value' => $mxId)) .
                    hidden(array('name' => 'skuId[]', 'value' => $material['id'])),
                    '销售订单',
                    $saleOrder['orderNo'],
                    $material['code'],
                    $material['name'],
                    $material['model'],
                    $unit,
                    text(array('name' => 'num[]', 'value' => $num, 'other' => self::digitalFiltering('numPrecision')." onblur=\"calculateMaterialTotal($('#materialMx'))\" ")),
                    text(array('name' => 'batch[]')),
                    $bom,
                    $routing,
                    text(array('name' => 'materialText[]')),
                ];
            }
            $json['warn'] = 2;
            $json['html'] = $this->tableMany(['tr' => $materialTr, 'thead' => false]);
        }
        return json_encode($json, 320);
    }

    /**
     * 获取工艺路线选项
     * @param $materialId
     * @param $allTypeId
     * @return array
     */
    public function routingOption($materialId, $allTypeId)
    {
        $option = [];
        $all    = find([
            'table' => 'routing',
            'where' => "coid = '" . self::$coid . "' and type = 1 and state = '启用' and materialId like '%$materialId%' order by materialId desc, time desc"
        ]);
        foreach ($all['array'] as $array) {
            $option[$array['id']] = $array['description'];
        }
        if (count($option) == 0) {
            $all = find([
                'table' => 'routing',
                'where' => "coid = '" . self::$coid . "' and type = 1 and state = '启用' and materialTypeId in ('" . implode("','", $allTypeId) . "') and materialId = '' order by time desc"
            ]);
            foreach ($all['array'] as $array) {
                $option[$array['id']] = $array['description'];
            }
        }
        return $option;
    }


    /**
     * 从销售退货单中选择
     *
     * @return bool|string
     */
    public function addRefundOrder(): bool|string
    {
        $html   =
            text(array("name" => "orderNo", "title" => "销售退货单号")) .
            text(array("name" => "code", "title" => "物料编码")) .
            text(array("name" => "materialName", "title" => "物料名称")) .
            text(array("name" => "model", "title" => "规格型号")) .
            hidden(array("name" => "id", "value" => $this->post('id'))) .
            hidden(array("name" => "soruceId", "value" => $this->post('sourceId') ? implode(',', $this->post('sourceId')) : ''));
        $button = "<input onclick=\"addMaterial('saleRefundOrder')\" type='button' class='searchSub m-l-5' value='提交'>";
        $html   = $this->search($html);
        return $this->layer(array("title" => "添加", "height" => 600, "width" => 1000, "html" => $html, "button" => $button));
    }

    /**
     * 从销售退货单中选择
     *
     * @return bool|string
     */
    public function addRefundOrderSearch(): bool|string
    {
//        $sourceId = $this->post('soruceId') ? explode(',', $this->post('soruceId')) : [];
        $id    = $this->post('id');
        $where = " a.coid='" . self::$coid . "' ";//and a.id not in ('" . implode("','", $sourceId) . "')
        $and   = "";
        $like  = [
            'b.orderNo' => $this->post('orderNo'),
            'c.name'    => $this->post('materialName'),
            'c.code'    => $this->post('code'),
            'c.model'   => $this->post('model'),
        ];
        $equal = [
            'b.state' => '已确认',
        ];

        $where .= search(["like" => $like, "=" => $equal]) . $and;
        $page  = $this->page([
            "table"   => "sale_refund_order_detail as a join sale_refund_order as b on a.orderId = b.id join material as c on c.id = a.materialId",
            'field'   => "a.*,c.name,c.code,c.model,b.orderNo",
            "where"   => $where,
            'pageNum' => 50,
            "order"   => "b.recordDate,a.`list` asc",
        ]);
        //获取表头
        $checkAll = $this->checkAll("saleRefundOrderForm", "mxId");
        $tr[]     = [$checkAll, '销售退货单号', '物料编码', '物料名称', '规格型号', '单位', '退货数量', '已生产数量'];
        //获取记录
        foreach ($page['array'] as $array) {
            //已生产数量（只要是在生产计划中就算）
            $machiningNum = 0;
            $all          = find(array('table' => self::table_material, 'where' => "coid = '" . self::$coid . "' and materialId = '$array[materialId]' and orderId !='$id' and sourceId = '$array[id]'"));
            foreach ($all['array'] as $val) {
                $machiningNum += self::material_num_change($array['materialId'], $val['unit'], $array['unit'], $val['num']);
            }
            $dominate = array(
                "box" => array("name" => "mxId", "value" => $array['id']),
            );
            $tr[]     = array(
                $this->dominate($dominate),
                $array['orderNo'],
                $array['code'],
                $array['name'],
                $array['model'],
                $array['unit'],
                self::lostZero($array['num'], 'numPrecision'),
                $machiningNum,
            );
        }
        $json['html'] = $page['hint'] .
            "<form name='saleRefundOrderForm'>" .
                hidden(array('name' => 'id', 'value' => $this->post('id'))) .
                $this->tableMany(array("tr" => $tr, "showSort" => true, "page" => $page['para'])) .
            "</form>" .
            $page['button'];
        return json_encode($json, 320);
    }

    /**
     * 从销售单中选择
     */
    public function addRefundOrderEdit(): bool|string
    {
        $id      = $this->post('id');
        $mxIdArr = $this->post('mxId');
        if (!$this->power(self::page, 'edit')) {
            $json['warn'] = '你还没有编辑权限';
        } elseif (static::is_null_array($mxIdArr)) {
            $json['warn'] = '请选择销售退货单产品';
        } else {
            $materialTr = [];
            foreach ($mxIdArr as $mxId) {
                $saleRefundOrderMx = query('sale_refund_order_detail', "id = '$mxId'");
                $saleRefundOrder   = query('sale_refund_order', "id = '$saleRefundOrderMx[orderId]'");
                $material          = query('material', "id = '$saleRefundOrderMx[materialId]'");
                $unitOption        = self::unitOption($material['id']);
                //已在生产单中的数量
                $machiningNum = 0;
                $all          = find(array(
                    'table' => self::table_material,
                    'where' => "coid = '" . self::$coid . "' and id !='$id' and sourceId = '$saleRefundOrder[id]' and materialId = '$material[id]'"
                ));
                foreach ($all['array'] as $val) {
                    $machiningNum += self::material_num_change($material['id'], $val['unit'], $material['unit'], $val['num']);
                }
                $num = ($saleRefundOrderMx['num'] - $machiningNum) < 0 ? 0 : self::formattingFigure($saleRefundOrderMx['num'] - $machiningNum, 'numPrecision');
                //默认bom
                $detaultBom = query('bom', "materialId = '$material[id]' and bomType like '%生产%' and state = '启用' and isDefault = 1");
                //默认工艺路线
                $routingId      = $this->defaultRouting($material['id']);
                $detaultRouting = query('routing', "id = '$routingId'");
                $id             = self::getId();
                $unit           = select(array('name' => 'unit[]', 'change' => 'unitChangeProduct', 'data' => ['unit' => $material['unit']], 'option' => $unitOption, 'value' => $material['unit'], 'title' => '单位'));
                $bom          = componentClass::selectText([
                    'name'      => 'bomId[]',
                    'value'     => $detaultBom['id'],
                    'title' => $detaultBom['bomVersion'],
                    'cla'       => self::page,
                    'fun'       => 'getBom',
                    'param'    => "{'materialId':'".$material['id']."'}",
                ]);
                $routing      = componentClass::selectText([
                    'name'      => 'routingId[]',
                    'value'     => $detaultRouting['id'],
                    'title' => $detaultRouting['description'],
                    'cla'       => self::page,
                    'fun'       => 'getRouting',
                    'param'    => "{'materialId':'".$material['id']."'}",
                ]);
                $materialTr[]   = [
                    componentClass::tablePperation('','delTableRow(this);calculateMaterialTotal($("#materialMx"))').
                    hidden(array('name' => 'productId[]', 'value' => self::getId())) .
                    hidden(array('name' => 'sourceNo[]', 'value' => $saleRefundOrder['orderNo'])) .
                    hidden(array('name' => 'sourceId[]', 'value' => $mxId)) .
                    hidden(array('name' => 'skuId[]', 'value' => $material['id'])),
                    '销售退货单',
                    $saleRefundOrder['orderNo'],
                    $material['code'],
                    $material['name'],
                    $material['model'],
                    $unit,
                    text(array('name' => 'num[]', 'value' => $num, 'other' => self::digitalFiltering('numPrecision')." onblur=\"calculateMaterialTotal($('#materialMx'))\" ")),
                    text(array('name' => 'batch[]')),
                    $bom,
                    $routing,
                    text(array('name' => 'materialText[]')),
                ];
            }
            $json['warn'] = 2;
            $json['html'] = $this->tableMany(['tr' => $materialTr, 'thead' => false]);
        }
        return json_encode($json, 320);
    }


    /**
     * @return bool|string
     */
    public function addMx()
    {
        $materialClassObj = new materialClass();
        $layerId          = $this->get('cla') . ucwords($this->get('fun'));
        $formName         = 'mxForm';
        $notAllowRepeat   = '';
        $data             = [
            'sub'            => " onclick=\"addRow();$('#" . $layerId . "').remove()\" ",
            'continueAdding' => $notAllowRepeat ? " onclick=\"addRow();removeSelectedContent('" . $formName . "')\" " : " onclick=\"addRow();\" ",
            'hidden'         => hidden(["name" => "notAllowRepeat", "value" => $notAllowRepeat]) .
                hidden(array('name' => 'materialId', 'value' => $this->post('materialId') ? implode(',', $this->post('materialId')) : '')),
        ];
        return $materialClassObj->addMx($data);
    }

    /**
     * 编辑
     *
     * @return bool|string
     */
    public function addMxEdit()
    {
        $mxIdArr = $this->post('mxId');
        if (!$this->power(self::page, 'edit')) {
            $json['warn'] = '你还没有编辑权限';
        } elseif (empty($mxIdArr) || !is_array($mxIdArr)) {
            $json['warn'] = '请选择物料';
        } else {
            $json['html'] = '';
            $tr           = [];
            foreach ($mxIdArr as $mxId) {
                $material     = query('material', "id = '$mxId'");
                $id           = self::getId();
                $unitOption   = self::unitOption($material['id']);
                $unit         = select(array(
                    'name' => 'unitDetail[]',
                    'change' => 'unitChange',
                    'data' => ['unit' => $material['unit']],
                    'option' => $unitOption,
                    'value' => $material['unit'],
                    'title' => '单位'
                ));
                $inventoryNum = self::inventory($material['id'], $material['unit']);
                $tr[]         = [
                    componentClass::tablePperation('','delTableRow(this);calculateTotal($("#materialTable"))').
                    hidden(array('name' => 'mxId[]', 'value' => $id)) .
                    hidden(array('name' => 'targetSkuId[]', 'value' => '')) .
                    hidden(array('name' => 'materialId[]', 'value' => $mxId)),
                    '',
                    '',
                    $material['code'],
                    $material['name'],
                    $material['model'],
                    $unit,
                    text(array('name' => 'mxNum[]', 'other' => self::digitalFiltering('numPrecision'), 'blur' => 'countNum')),
                    text(array('name' => 'loss[]', 'other' => self::digitalFiltering('lossRate'), 'class' => 'text', 'blur' => 'countNum')),
                    "<span class='totalNum'>0</span>",
                    "<span class='inventory'>" . $inventoryNum . "</span>",
                    text(array('name' => 'mxText[]', 'class' => 'text textPrice')),
                ];
            }
            $json['html'] = $this->tableMany(array('tr' => $tr, 'thead' => false));
            $json['warn'] = 2;
        }
        return json_encode($json, 320);
    }


    /**
     *选择生产产品
     *
     * @return bool|string
     */
    public function addMaterial(): bool|string
    {
        $materialClassObj = new materialClass();
        $layerId          = $this->get('cla') . ucwords($this->get('fun'));
        $formName         = 'materialMxForm';
        $notAllowRepeat   = 1;
        $data             = [
            'sub'            => " onclick=\"addMaterial();$('#" . $layerId . "').remove()\" ",
            'continueAdding' => $notAllowRepeat ? " onclick=\"addMaterial();removeSelectedContent('" . $formName . "')\" " : " onclick=\"addMaterial();\" ",
            'hidden'         => hidden(["name" => "notAllowRepeat", "value" => $notAllowRepeat]) .
                hidden(["name" => "supplyControl", "value" => '允许生产']) .
                hidden(["name" => "formName", "value" => $formName]) .
                hidden(array('name' => 'materialId', 'value' => $this->post('skuId') ? implode(',', $this->post('skuId')) : '')),
        ];
        return $materialClassObj->addMx($data);
    }

    /**
     * 编辑
     *
     * @return bool|string
     */
    public function addMaterialEdit(): bool|string
    {
        $materialIdArr = $this->post('mxId');
        if (!$this->power(self::page, 'edit')) {
            $json['warn'] = '你还没有编辑权限';
        } elseif (static::is_null_array($materialIdArr)) {
            $json['warn'] = '请选择物料';
        } else {
            $materialTr = [];
            foreach ($materialIdArr as $materialId) {
                $material    = query('material', "id = '$materialId'");
                $unitOption  = self::unitOption($material['id']);
                //默认bom
                $detaultBom = query('bom', "materialId = '$material[id]' and bomType like '%生产%' and state = '启用' and isDefault = 1");
                //默认工艺路线
                $routingId      = $this->defaultRouting($material['id']);
                $detaultRouting = query('routing', "id = '$routingId'");
                $unit           = select(array(
                    'name' => 'unit[]',
                    'change' => 'unitChangeProduct',
                    'data' => ['unit' => $material['unit']],
                    'option' => $unitOption,
                    'value' => $material['unit'],
                    'title' => '单位'
                ));
                $bom          = componentClass::selectText([
                    'name'      => 'bomId[]',
                    'value'     => $detaultBom['id'],
                    'title' => $detaultBom['bomVersion'],
                    'cla'       => self::page,
                    'fun'       => 'getBom',
                    'param'    => "{'materialId':'".$material['id']."'}",
                ]);
                $routing      = componentClass::selectText([
                    'name'      => 'routingId[]',
                    'value'     => $detaultRouting['id'],
                    'title' => $detaultRouting['description'],
                    'cla'       => self::page,
                    'fun'       => 'getRouting',
                    'param'    => "{'materialId':'".$material['id']."'}",
                ]);
                $materialTr[]   = [
                    componentClass::tablePperation('','delTableRow(this);calculateMaterialTotal($("#materialMx"))').
                    hidden(array('name' => 'productId[]', 'value' => self::getId())) .
                    hidden(array('name' => 'sourceNo[]')) .
                    hidden(array('name' => 'sourceId[]')) .
                    hidden(array('name' => 'skuId[]', 'value' => $material['id'])),
                    '',
                    '',
                    $material['code'],
                    $material['name'],
                    $material['model'],
                    $unit,
                    text(array('name' => 'num[]', 'other' => self::digitalFiltering('numPrecision')." onblur=\"calculateMaterialTotal($('#materialMx'))\" ")),
                    text(array('name' => 'batch[]')),
                    $bom,
                    $routing,
                    text(array('name' => 'materialText[]')),
                ];
            }
            $json['warn'] = 2;
            $json['html'] = $this->tableMany(['tr' => $materialTr, 'thead' => false]);
        }
        return json_encode($json, 320);
    }

    /**
     * 启用/停用
     */
    public function stateSwitch(): bool|string
    {
        $isClose = $this->post('type');
        //生成表单
        $html = $this->layerEdit(array(
            "table"     => array(
                array(
                    "提示：" => "是否确认" . ($isClose == 1 ? '关闭' : '开启') . "此订单？",
                ),
            ), 'hidden' => hidden(array('name' => 'type', 'value' => $isClose)),
        ));
        //返回
        return $this->layer(array("title" => "订单状态", "height" => 165, "html" => $html, 'formTwo' => 'listForm'));
    }

    /**
     * 启用/停用
     */
    public function stateSwitchEdit(): bool|string
    {
        $idArr   = $this->post('id') ? [$this->post('id')] : $this->post('listId');
        $isClose = $this->post('type');
        if (!$this->power(self::page, "edit")) {
            $json['warn'] = "你没有" . self::title . "的编辑权限";
        } elseif (empty($idArr) || !is_array($idArr)) {
            $json['warn'] = "请选择数据";
        } else {
            $type  = $isClose == 1 ? '关闭' : '开启';
            $field = [
                'isClose' => $isClose,
            ];
            $x     = 0;
            $text  = '';
            foreach ($idArr as $id) {
                $machiningOrder = query(self::table, "id = '$id'");
                $json['warn']   = update(array('table' => self::table, 'field' => $field, 'where' => " id ='$id'"));
                if (in_array($json['warn'], ["新增成功", "更新成功"])) {
                    $x++;
                    if ($isClose == 1) {
                        materialDistributeClass::avgInventoryPrice($machiningOrder['id']);
                    }
                    self::insertOperateLog($id,self::title,$type);
                    //添加日志
                    $text .= $this->control['name'] . $type . "了生产计划单：单号：【{$machiningOrder['orderNo']}】 id：【" . $id . '】';
                } else {
                    $text .= $this->control['name'] . $type . "了生产计划单：单号：【{$machiningOrder['orderNo']}】 id：【" . $id . '】失败';
                }

            }
            if ($text) {
                $this->logText(["type" => self::page, "text" => $text]);
            }
            //返回
            $_SESSION['warn'] = $type . '成功';
            $json['warn']     = 2;
        }
        //返回
        return json_encode($json, 320);
    }


    /*详情页*/
    public function detail(): string
    {
        $info = query(self::table, " id = '{$this->get('id')}'");
        if (empty($info['id'])) {
            $html = '未找到此' . self::title;
        } else {
            //菜单
            $data = array(
                "id"   => $info['id'],
                'menu' => [
                    'detail'        => '基本信息',
                    'materialTrack' => '物料追溯',
                    'qualityTrack'  => '质量追溯',
                    'semiOrder'     => '半成品进度',
                ]
            );

            $button = [];
            if ($this->power(self::page, "confirm") && $info['state'] == '待确认') {
                $button[] = [
                    "name"  => "复核",
                    "type"  => "弹出层",
                    "cla"   => $this->get('cla'),
                    "fun"   => "operate",
                    "post"  => ["act" => 'confirm', "id" => $info['id']],
                    'style' => 'li-btn-blue-bg',
                ];
            }
            if ($info['state'] == '草稿') {
                if ($this->power(self::page, "edit")) {
                    //按钮
                    $button[] = [
                        "name"  => "提交",
                        "type"  => "弹出层",
                        "cla"   => $this->get('cla'),
                        "fun"   => "operate",
                        "post"  => ["act" => 'commit', "id" => $info['id']],
                        'style' => 'li-btn-blue-bg',
                    ];
                    //按钮
                    $button[] = [
                        "name"  => "编辑",
                        "type"  => "顶部页卡",
                        "title" => "编辑用户",
                        "url"   => $this->root . "control/adIndex.html?cla={$this->get('cla')}&fun=edit&id=" . $info['id'],

                    ];
                }
                if ($this->power(self::page, "del")) {
                    //按钮
                    $button[] = [
                        "name" => "删除",
                        "type" => "弹出层",
                        "cla"  => $this->get('cla'),
                        "fun"  => "operate",
                        "post" => ["act" => 'del', "id" => $info['id']],
                    ];
                }
            }
            //按钮
            $button[] = [
                "name" => "打印",
                "type" => "点击事件",
                "fun"  => "window.open('" . root . "control/adIndex.html?cla=" . self::page . "&fun=printPage&id={$info['id']}')",
            ];


            if ($this->power(self::page, "cancel") && $info['state'] != '草稿' && !$info['isClose']) {
                $button[] = [
                    "name" => "撤回",
                    "type" => "弹出层",
                    "cla"  => $this->get('cla'),
                    "fun"  => "operate",
                    "post" => ["act" => 'cancel', "id" => $info['id']],
                ];
            }
            if ($this->power(self::page, "edit") && $info['state'] == '已确认') {
                //按钮
                $isClose  = $info['isClose'] == 1 ? 0 : 1;
                $name     = $info['isClose'] == 1 ? '开启' : '关闭';
                $button[] = [
                    "name" => $name,
                    "type" => "点击事件",
                    "fun"  => "layer('" . self::page . "','stateSwitch',{id:'" . $info['id'] . "',type:'{$isClose}'})",
                ];
            }
            $button[] = [
                "name"  => "单据追踪",
                "type"  => "顶部页卡",
                "title" => "单据追踪",
                "url"   => $this->root . "control/adIndex.html?cla=orderTrack&fun=home&orderType=生产计划单&id=" . $info['id'],
            ];
            //操作记录
            $button[] = self::operateLogBut($info['id'],1);

            $tr[]   = ['源单编号', '产品名称', '物料编码', '物料名称', '规格型号', '单位', '额定数量', '损耗率%', '预计用量', '当前可用库存', '备注'];
            $result = find(array('table' => self::tableMx, 'where' => "orderId = '$info[id]' order by list"));
            $total = ['num'=>0,'quantity'=>0,'inventory'=>0];
            foreach ($result['array'] as $array) {
                $material           = query('material', " id = '$array[materialId]'");
                $inventoryNum       = self::inventory($array['materialId'], $array['unit']);
                $machining_order_mx = query('machining_order_mx', " id = '$array[targetSkuId]'");
                $tr[]               = [
                    $machining_order_mx['sourceNo'],
                    materialClass::getName($machining_order_mx['materialId'], short: true),
                    $material['code'],
                    $material['name'],
                    $material['model'],
                    $array['unit'],
                    self::lostZero($array['num'], 'numPrecision'),
                    ($array['loss'] * 100) . '%',
                    self::lostZero($array['quantity'], 'numPrecision'),
                    $inventoryNum,
                    $array['text'],
                ];
                $total['num'] += $array['num'];
                $total['quantity'] += $array['quantity'];
                $total['inventory'] += $inventoryNum;
            }
            $total_tr = [
                6 => self::lostZero($total['num'], 'numPrecision'),
                8 => self::lostZero($total['quantity'], 'numPrecision'),
                9 => self::lostZero($total['inventory'], 'numPrecision'),
            ];

            $materialTr[] = ['源单类型', '源单编号', '产品编码', '产品名称', '规格型号', '单位', '计划数量', '生产批次', 'BOM版本', '工艺路线', '备注'];
            $result       = find(array('table' => self::table_material, 'where' => "orderId = '$info[id]' order by list"));
            foreach ($result['array'] as $array) {
                $material    = query('material', " id = '$array[materialId]'");
                $relativeNo  = $relativeType = '';
                if ($array['sourceTable'] == 'product_demands') {
                    $productDemand = query('product_demands', "id = '$array[sourceId]'");
                    $sale_order    = query('sale_order', "id = '$productDemand[sourceId]'");
                    $relativeNo    = $sale_order['orderNo'];
                    $relativeType  = '销售订单';
                } elseif ($array['sourceTable'] == 'sale_refund_order_detail') {
                    $saleRefundOrderMx = query('sale_refund_order_detail', "id = '$array[sourceId]'");
                    $saleRefundOrder   = query('sale_refund_order', "id = '$saleRefundOrderMx[orderId]'");
                    $relativeNo        = $saleRefundOrder['orderNo'];
                    $relativeType      = '销售退货单';
                } elseif ($array['sourceTable'] == 'sale_order_detail') {
                    $saleOrderMx  = query('sale_order_detail', "id = '$array[sourceId]'");
                    $saleOrder    = query('sale_order', "id = '$saleOrderMx[orderId]'");
                    $relativeNo   = $saleOrder['orderNo'];
                    $relativeType = '销售订单';
                } elseif ($array['sourceTable'] == 'sale_forecast_detail') {
                    $saleOrderMx  = query('sale_forecast_detail', "id = '$array[sourceId]'");
                    $saleOrder    = query('sale_forecast', "id = '$saleOrderMx[orderId]'");
                    $relativeNo   = $saleOrder['orderNo'];
                    $relativeType = '销售预测单';
                }
                $bom          = query('bom', "id = '$array[bomId]'");
                $routing      = query('routing', "id = '$array[routingId]'");
                $materialTr[] = [
                    $relativeType,
                    $relativeNo,
                    $material['code'],
                    $material['name'],
                    $material['model'],
                    $array['unit'],
                    self::lostZero($array['num'], 'numPrecision'),
                    $array['batch'],
                    $bom['bomVersion'],
                    $routing['description'],
                    $array['text'],
                ];
            }
            $line = query('product_line', "id = '$info[lineId]'");
            //生成表单
            $data['button'] = $this->tableShowButton($button);
            $data['html'] = $this->tableShow(array(
                array(
                    array(
                        "生产部门"     => $line['workshop'] . '-' . $line['name'],
                        "计划开工日期" => $info['startDate'],
                        "计划完工日期" => $info['endDate'],
                        "单据编号" => $info['orderNo'],
                    ),
                    array(
                        "制单人"   => self::findStaff($info['stid']),
                        "状态"     => $info['state']
                    ),
                    array(
                        "备注"         => pre($info['text']),
                    ),
                ),
            ));
            $data['html'] .= $this->detailList([
                'title' => '产品明细',
                'html' => $this->listTable(array(
                    'tr' => $materialTr,
                    'showSort' => true,
                    'total' => [6=>self::lostZero(array_sum(array_column((array)$result['array'],'num')),'num')]
                )),
            ]);
            $data['html'] .= $this->detailList([
                'title' => '所需物料',
                'html' => $this->listTable(array('tr' => $tr, 'showSort' => true, 'total' => $total_tr)),
            ]);
            //生成页面
            $html = $this->tab($data);
        }
        //返回
        return $html;
    }

    /**
     * 不能小于0
     */
    public static function notLessThanZero($value)
    {
        if ($value && $value < 0) {
            return 0;
        }
        return $value;
    }

    /**
     * 确认
     */
    public function confirmEdit()
    {
        $id    = $this->post('id');
        $info  = query(self::table, " id = '$id'");
        $field = [
            'state'       => '已确认',
            'checkId'     => self::$stid,
            'confirmTime' => $this->time,
        ];
        if (!$this->power(self::page, 'confirm')) {
            $json['warn'] = '你还没有确认权限';
        } elseif (empty($info['id'])) {
            $json['warn'] = '未找到此生产计划';
        } elseif ($info['state'] != '待确认') {
            $json['warn'] = '此生产计划不是待确认状态，不能确认';
        } else {
            $this->startTrans();
            $json['warn'] = update(array(
                "table" => self::table,
                "field" => $field,
                "where" => "id = '$info[id]'",
            ));

            if (optionSettingClass::getSettingItem('生产', 'autoNewSendOrder') && optionSettingClass::getSettingItem('生产', 'productCtrlMethod') == 'machiningSendOrder') {
                //生成派工单
                $bool = $this->creatSendOrder($id);
                if ($bool !== true) {
                    $json['warn'] = $bool;
                }
            } elseif (optionSettingClass::getSettingItem('生产', 'productCtrlMethod') == 'productCard') {
                //生成生产流转卡

                $bool = $this->creatCard($id);
                if ($bool !== true) {
                    $json['warn'] = $bool;
                }
            }

            //生成半成品需求计划
            $bool = $this->semiDemands($id);
            if ($bool !== true) {
                $json['warn'] = $bool;
            }
            //生成委外需求计划
            $bool = $this->outsourceDemands($id);
            if ($bool !== true) {
                $json['warn'] = $bool;
            }
            //生成材料需求计划
            $bool = $this->materialDemands($id);
            if ($bool !== true) {
                $json['warn'] = $bool;
            }
            //生成配料需求
            $bool = $this->ingredientsDemands($id);
            if ($bool !== true) {
                $json['warn'] = $bool;
            }

            if (in_array($json['warn'], ["新增成功", "更新成功"])) {
                $this->commitTrans();
                // 单据是否需要复核
                if(!in_array(self::page,self::$publicSettings['documentReview'])) {
                    self::insertOperateLog($info['id'],self::title,"提交、复核");
                }else{
                    self::insertOperateLog($info['id'],self::title,"复核");
                }
                //添加日志
                $type = mb_substr($json['warn'], 0, 2, 'utf-8');
                $text = $this->control['name'] . $type . "了" . self::title . "id：【" . $id . '】';
                $this->logText(["type" => self::page, "text" => $text]);
                //返回
                $_SESSION['warn'] = $json['warn'];
                $json['warn']     = 2;
                $json['href']     = root . "control/adIndex.html?cla=" . $this->get('cla') . "&fun=detail&id=" . $info['id'];
            } else {
                $this->rollback();
            }
        }
        resp:
        return json_encode($json, 320);
    }

    /**
     * 获取所有中间层的自制/材料采购件(主单位)
     */
    public static function semiBomMx($bomId, $materialId, $num, $unit, $source = '自制', &$material_list = [])
    {
        $bom = query('bom', "id = '$bomId' and state = '启用'");
        if (empty($bom['id'])) {
            self::log(["type" => self::page, "text" => "未找到BOM表"]);
            return $material_list;
        }
        $material    = query('material', "id = '$materialId'");//生产产品
        //BOM表头物料单位向物料主单位转化
        if ($bom['unit'] == $material['unit_one']) {
            $bom['num'] = $material['unit_one_num'] * $bom['num'];
        } elseif ($bom['unit'] == $material['unit_two']) {
            $bom['num'] = $material['unit_two_num'] * $bom['num'];
        } elseif ($bom['unit'] != $material['unit']) {
            self::log(["type" => self::page, "text" => "BOM表头物料单位不在物料的单位中"]);
            return $material_list;
        }
        //生产单位向主单位转化
        if ($unit == $material['unit']) {
            $pcs = $bom['num'] > 0 ? ($num / $bom['num']) : 0;//需要几个BOM的子物料
        } elseif ($unit == $material['unit_one']) {
            $num = $num * $material['unit_one_num'];
            $pcs = $bom['num'] > 0 ? ($num / $bom['num']) : 0;//需要几个BOM的子物料
        } elseif ($unit == $material['unit_two']) {
            $num = $num * $material['unit_two_num'];
            $pcs = $bom['num'] > 0 ? ($num / $bom['num']) : 0;//需要几个BOM的子物料
        } else {
            self::log(["type" => self::page, "text" => "物料【{$material['code']}-{$material['name']}-{$material['model']}】生产单位不在物料的单位中"]);
            return $material_list;
        }
        $bomMx = find(array('table' => 'bom_detail', 'where' => "orderId = '$bom[id]' order by list"));
        foreach ($bomMx['array'] as $array) {
            $childMaterial = query('material', "id = '$array[materialId]'");
            $array['num']  = self::material_num_change($array['materialId'], $array['unit'], $childMaterial['unit'], $array['num']);
            if ($childMaterial['source'] == $source) {
                $production = 0;
                if ($source == '自制') {
                    $production = $childMaterial['production'];
                } elseif ($source == '采购') {
                    $production = $childMaterial['procurement'];
                }
                $material_list[] = array('materialId' => $array['materialId'], 'num' => $array['num'] * (1 + $array['loss']) * $pcs, 'production' => $production);
            }
            //下一层
            if (!empty($array['bomId'])) {
                $material_list = self::semiBomMx($array['bomId'], $array['materialId'], $array['num'] * (1 + $array['loss']) * $pcs, $array['unit'], $source, $material_list);
            }
        }
        return $material_list;
    }


    /**
     * 获取所有中间层的自制/材料采购件(带生产周期)
     */
    public static function semiBomMxWithProduction($bomId, $materialId, $num, $unit, $source = '自制', $productionTotal = 0, &$material_list = [])
    {
        $bom = query('bom', "id = '$bomId' and state = '启用'");
        if (empty($bom['id'])) {
            self::log(["type" => self::page, "text" => "未找到BOM表"]);
            return $material_list;
        }
        $material    = query('material', "id = '$materialId'");//生产产品
        //BOM表头物料单位向物料主单位转化
        if ($bom['unit'] == $material['unit_one']) {
            $bom['num'] = $material['unit_one_num'] * $bom['num'];
        } elseif ($bom['unit'] == $material['unit_two']) {
            $bom['num'] = $material['unit_two_num'] * $bom['num'];
        } elseif ($bom['unit'] != $material['unit']) {
            self::log(["type" => self::page, "text" => "BOM表头物料单位不在物料的单位中"]);
            return $material_list;
        }
        //生产单位向主单位转化
        if ($unit == $material['unit']) {
            $pcs = $bom['num'] > 0 ? ($num / $bom['num']) : 0;//需要几个BOM的子物料
        } elseif ($unit == $material['unit_one']) {
            $num = $num * $material['unit_one_num'];
            $pcs = $bom['num'] > 0 ? ($num / $bom['num']) : 0;//需要几个BOM的子物料
        } elseif ($unit == $material['unit_two']) {
            $num = $num * $material['unit_two_num'];
            $pcs = $bom['num'] > 0 ? ($num / $bom['num']) : 0;//需要几个BOM的子物料
        } else {
            self::log(["type" => self::page, "text" => "物料【{$material['code']}-{$material['name']}-{$material['model']}】生产单位不在物料的单位中"]);
            return $material_list;
        }
        $bomMx = find(array('table' => 'bom_detail', 'where' => "orderId = '$bom[id]' order by list"));
        foreach ($bomMx['array'] as $array) {
            $childMaterial = query('material', "id = '$array[materialId]'");
            $array['num']  = self::material_num_change($array['materialId'], $array['unit'], $childMaterial['unit'], $array['num']);
            $production    = 0;
            if ($childMaterial['source'] == $source) {
                if ($source == '自制') {
                    $production = $childMaterial['production'];
                } elseif ($source == '采购') {
                    $production = $childMaterial['procurement'];
                }
                $material_list[] = array('materialId' => $array['materialId'], 'num' => $array['num'] * (1 + $array['loss']) * $pcs, 'production' => $production + $productionTotal);
            }
            //下一层
            if (!empty($array['bomId'])) {
                $material_list = self::semiBomMxWithProduction($array['bomId'], $array['materialId'], $array['num'] * (1 + $array['loss']) * $pcs, $array['unit'], $source, $production + $productionTotal, $material_list);
            }
        }
        return $material_list;
    }

    /**
     * 计算一层BOM用料(主单位，配料用)
     */
    public static function singleBomMx($bomId, $materialId, $num, $unit, &$material_list = [])
    {
        $bom = query('bom', "id = '$bomId' and state = '启用'");
        if (empty($bom['id'])) {
            self::log(["type" => self::page, "text" => "未找到BOM表"]);
            return $material_list;
        }
        $material    = query('material', "id = '$materialId'");//生产产品
        //BOM表头物料单位向物料主单位转化
        if ($bom['unit'] == $material['unit_one']) {
            $bom['num'] = $material['unit_one_num'] * $bom['num'];
        } elseif ($bom['unit'] == $material['unit_two']) {
            $bom['num'] = $material['unit_two_num'] * $bom['num'];
        } elseif ($bom['unit'] != $material['unit']) {
            self::log(["type" => self::page, "text" => "BOM表头物料单位不在物料的单位中"]);
            return $material_list;
        }
        //生产单位向主单位转化
        if ($unit == $material['unit']) {
            $pcs = $bom['num'] > 0 ? ($num / $bom['num']) : 0;//需要几个BOM的子物料
        } elseif ($unit == $material['unit_one']) {
            $num = $num * $material['unit_one_num'];
            $pcs = $bom['num'] > 0 ? ($num / $bom['num']) : 0;//需要几个BOM的子物料
        } elseif ($unit == $material['unit_two']) {
            $num = $num * $material['unit_two_num'];
            $pcs = $bom['num'] > 0 ? ($num / $bom['num']) : 0;//需要几个BOM的子物料
        } else {
            self::log(["type" => self::page, "text" => "物料【{$material['code']}-{$material['name']}-{$material['model']}】生产单位不在物料的单位中"]);
            return $material_list;
        }
        $bomMx = find(array('table' => 'bom_detail', 'where' => "orderId = '$bom[id]' and pickType = '配料' order by list"));
        foreach ($bomMx['array'] as $array) {
            $material     = query('material', "id = '$array[materialId]'");
            $array['num'] = self::material_num_change($array['materialId'], $array['unit'], $material['unit'], $array['num']);
            //只找一层
            if ($material['source'] == '虚拟') {
                //跳过虚拟件
                $material_list = self::singleBomMx($array['bomId'], $array['materialId'], $array['num'] * (1 + $array['loss']) * $pcs, $array['unit'], $material_list);
            } else {
                $material_list[] = array('materialId' => $array['materialId'], 'num' => $array['num'] * $pcs * (1 + $array['loss']), 'warehouseId' => $array['warehouseId']);
            }
        }
        return $material_list;
    }
    /**
     * 计算BOM用料(主单位)
     */
    public static function bomMx($bomId, $materialId, $num, $unit, &$material_list = [])
    {
        $bom = query('bom', "id = '$bomId' and state = '启用'");
        if (empty($bom['id'])) {
            self::log(["type" => self::page, "text" => "未找到BOM表"]);
            return $material_list;
        }
        $material    = query('material', "id = '$materialId'");//生产物料
        //BOM表头物料单位向物料主单位转化
        if ($bom['unit'] == $material['unit_one']) {
            $bom['num'] = $material['unit_one_num'] * $bom['num'];
        } elseif ($bom['unit'] == $material['unit_two']) {
            $bom['num'] = $material['unit_two_num'] * $bom['num'];
        } elseif ($bom['unit'] != $material['unit']) {
            self::log(["type" => self::page, "text" => "BOM表头物料单位不在物料的单位中"]);
            return $material_list;
        }
        //生产单位向主单位转化
        if ($unit == $material['unit']) {
            $pcs = $bom['num'] > 0 ? ($num / $bom['num']) : 0;//需要几个BOM的子物料
        } elseif ($unit == $material['unit_one']) {
            $num = $num * $material['unit_one_num'];
            $pcs = $bom['num'] > 0 ? ($num / $bom['num']) : 0;//需要几个BOM的子物料
        } elseif ($unit == $material['unit_two']) {
            $num = $num * $material['unit_two_num'];
            $pcs = $bom['num'] > 0 ? ($num / $bom['num']) : 0;//需要几个BOM的子物料
        } else {
            self::log(["type" => self::page, "text" => "物料【{$material['code']}-{$material['name']}-{$material['model']}】生产单位不在物料的单位中"]);
            return $material_list;
        }
        $bomMx = find(array('table' => 'bom_detail', 'where' => "orderId = '$bom[id]' order by list"));
        foreach ($bomMx['array'] as $array) {
            $material     = query('material', "id = '$array[materialId]'");
            $array['num'] = self::material_num_change($array['materialId'], $array['unit'], $material['unit'], $array['num']);
            //只找一层
            if ($material['source'] == '虚拟') {
                $material_list = self::bomMx($array['bomId'], $array['materialId'], $array['num'] * (1 + $array['loss']) * $pcs, $array['unit'], $material_list);
            } else {
                $material_list[] = array('materialId' => $array['materialId'], 'quantity' => $array['num'] * (1 + $array['loss']) * $pcs, 'num' => $array['num'] * $pcs);
            }
        }
        return $material_list;
    }


    /**
     * 计算一层BOM用料(主单位,无限制)
     */
    public static function oneBomMx($bomId, $materialId, $num, $unit, &$material_list = [])
    {
        return self::bomMx($bomId, $materialId, $num, $unit, $material_list);
    }

    /**
     * 配料需求
     */
    public function ingredientsDemands($id): bool|string
    {
        $info          = query(self::table, " id = '$id'");
        $all           = find(array('table' => self::table_material, "where" => "orderId = '$id' order by list asc"));
        $material_list = [];
        foreach ($all['array'] as $array) {
            if (!empty($array['bomId'])) {
                $result_list   = self::singleBomMx($array['bomId'], $array['materialId'], $array['num'], $array['unit']);
                $material_list = $this->merge_material_two($material_list, $result_list);
            }
        }
        foreach ($material_list as $array) {
            $material    = query('material', "id = '$array[materialId]'");
            $warn        = static::insert(array(
                'table' => 'ingredients_demands',
                'field' => [
                    'id'               => suiji(),
                    'coid'             => self::$coid,
                    'machiningOrderId' => $info['id'],
                    'materialId'       => $material['id'],
                    'warehouseId'      => $array['warehouseId'],
                    'lineId'           => $info['lineId'],
                    'num'              => self::formattingFigure($array['num'], 'numPrecision'),
                    'unit'             => $material['unit'],
                ]
            ));
            if ($warn != '新增成功') {
                return '生成配料需求失败';
            }
        }
        return true;
    }

    /**
     * 生成半成品需求计划
     */
    private function semiDemands($id): bool|string
    {
        $info          = query(self::table, " id = '$id'");
        $all           = find(array('table' => self::table_material, "where" => "orderId = '$id' order by list asc"));
        $material_list = [];
        foreach ($all['array'] as $array) {
            if (!empty($array['bomId'])) {
                $result_list   = self::semiBomMxWithProduction($array['bomId'], $array['materialId'], $array['num'], $array['unit']);
                $material_list = $this->merge_material_production($material_list, $result_list);
            }

        }
        foreach ($material_list as $array) {
            $material    = query('material', "id = '$array[materialId]'");
            $needTime    = date('Y-m-d H:i:s', strtotime($info['startDate']) - 3600 * $array['production']);
            $warn        = static::insert(array(
                'table' => 'semi_demands',
                'field' => [
                    'id'            => suiji(),
                    'coid'          => self::$coid,
                    'sourceTable'   => 'machining_order',
                    'sourceId'      => $info['id'],
                    'materialId'    => $material['id'],
                    'num'           => self::formattingFigure($array['num'], 'numPrecision'),
                    'unit'          => $material['unit'],
                    'production'    => $array['production'],
                    'needTime'      => $needTime
                ]
            ));
            if ($warn != '新增成功') {
                return '生成半成品需求计划失败';
            }
        }
        return true;
    }

    /**
     * 委外需求计划
     * @param $id
     * @return bool|string
     */
    private function outsourceDemands($id)
    {
        $info          = query(self::table, " id = '$id'");
        $all           = find(array('table' => self::table_material, "where" => "orderId = '$id' order by list asc"));
        $material_list = [];
        foreach ($all['array'] as $array) {
            if (!empty($array['bomId'])) {
                $result_list   = self::semiBomMx($array['bomId'], $array['materialId'], $array['num'], $array['unit'], '委外');
                $material_list = $this->merge_material($material_list, $result_list);
            }
        }
        foreach ($material_list as $array) {
            $material    = query('material', "id = '$array[materialId]'");
            $warn        = static::insert(array(
                'table' => 'outsource_demands',
                'field' => [
                    'id'            => suiji(),
                    'coid'          => self::$coid,
                    'sourceType'    => '生产计划单',
                    'sourceTable'   => 'machining_order',
                    'sourceId'      => $info['id'],
                    'sourceDate'    => $info['recordDate'],
                    'sourceNo'      => $info['orderNo'],
                    'materialId'    => $material['id'],
                    'num'           => self::formattingFigure($array['num'], 'numPrecision'),
                    'unit'          => $material['unit'],
                ]
            ));
            if ($warn != '新增成功') {
                return '生成委外需求计划失败';
            }
        }
        return true;
    }

    /**
     * 材料需求计划
     */
    private function materialDemands($id)
    {
        $info          = query(self::table, " id = '$id'");
        $all           = find(array('table' => self::table_material, "where" => "orderId = '$id' order by list asc"));
        $material_list = [];
        foreach ($all['array'] as $array) {
            $result_list   = self::semiBomMxWithProduction($array['bomId'], $array['materialId'], $array['num'], $array['unit'], '采购');
            $material_list = $this->merge_material_production($material_list, $result_list);
        }
        foreach ($material_list as $array) {
            $material    = query('material', "id = '$array[materialId]'");
            $needTime    = date('Y-m-d H:i:s', strtotime($info['startDate']) - 3600 * $array['production']);
            $warn        = static::insert(array(
                'table' => 'machining_demands',
                'field' => [
                    'id'            => suiji(),
                    'coid'          => self::$coid,
                    'sourceTable'   => 'machining_order',
                    'sourceId'      => $info['id'],
                    'materialId'    => $material['id'],
                    'num'           => self::formattingFigure($array['num'], 'numPrecision'),
                    'unit'          => $material['unit'],
                    'production'    => $array['production'],
                    'needTime'      => $needTime
                ]
            ));
            if ($warn != '新增成功') {
                return '生成材料需求计划失败';
            }
        }
        $all = find(array('table' => self::tableMx, "where" => "orderId = '$id' order by list asc"));
        foreach ($all['array'] as $array) {
            if (empty($array['targetSkuId'])) {
                $material    = query('material', "id = '$array[materialId]'");
                $warn        = static::insert(array(
                    'table' => 'machining_demands',
                    'field' => [
                        'id'            => suiji(),
                        'coid'          => self::$coid,
                        'sourceTable'   => 'machining_order',
                        'sourceId'      => $info['id'],
                        'materialId'    => $material['id'],
                        'num'           => self::formattingFigure($array['num'], 'numPrecision'),
                        'unit'          => $material['unit'],
                    ]
                ));
                if ($warn != '新增成功') {
                    return '生成材料需求计划失败';
                }
            }
        }
        return true;
    }

    /**
     * 生成生产流转卡
     * @param $id
     * @param string $orderType
     * @param string $page
     * @return bool|string
     */
    public function creatCard($id, $orderType = '生产计划单', $page = self::page): bool|string
    {
        $info = query(self::table, " id = '$id'");
        $all  = find(array('table' => self::table_material, "where" => "orderId = '$id' order by list asc"));
        foreach ($all['array'] as $index => $array) {
            $material         = query('material', " id = '$array[materialId]'");
            $field            = [
                'id'            => self::getId(),
                'coid'          => self::$coid,
                'orderType'     => $orderType,
                'recordDate'    => $info['recordDate'],
                'routingId'     => $array['routingId'],
                'lineId'        => $info['lineId'],
                'sourceId'      => $info['id'],
                'sourceMxId'    => $array['id'],
                'materialId'    => $array['materialId'],
                'unit'          => $array['unit'],
                'num'           => $array['num'],
                'sourceNo'      => $info['orderNo'],
                'stid'          => self::$stid,
            ];
            $userNo           = codeTemplateClass::generateCode($page, [
                'prefix' => 'SCLZ' . date('Ymd', strtotime($info['recordDate'])),
                'table'  => 'machining_card',
                'field'  => 'orderNo',
                'where'  => " and coid = '" . self::$coid . "'",
                'index'  => $index + 1
            ], $field);
            $field['orderNo'] = $userNo;
            $warn             = static::insert(array(
                'table' => 'machining_card',
                'field' => $field,
            ));
            if ($warn != '新增成功') {
                return "产品【{$material['code']}-{$material['name']}-{$material['model']}】生成生产流转卡失败";
            }

            $routingDetail = find(array('table' => 'routing_detail', 'where' => "orderId = '$array[routingId]' order by list asc"));
            foreach ($routingDetail['array'] as $list => $val) {
                if ($val['isOut'] == '否') {
                    $mxField = [
                        'id'          => suiji(),
                        'coid'        => self::$coid,
                        'orderId'     => $field['id'],
                        'processId'   => $val['processId'],
                        'routingMxId' => $val['id'],
                        'list'        => $list + 1
                    ];
                    $warn    = static::insert(array(
                        'table' => 'machining_card_detail',
                        'field' => $mxField,
                    ));
                    if ($warn != '新增成功') {
                        return "产品【{$material['code']}-{$material['name']}-{$material['model']}】生成生产流转卡明细失败";
                    }
                }
            }
        }
        return true;
    }

    /**
     * 生成派工单
     */
    public function creatSendOrder($id): bool|string
    {
        $info = query(self::table, " id = '$id'");
        $all  = find(array('table' => self::table_material, "where" => "orderId = '$id' order by list asc"));
        foreach ($all['array'] as $array) {
            $routingDetail = find(array('table' => 'routing_detail', 'where' => "orderId = '$array[routingId]' order by list asc"));
            foreach ($routingDetail['array'] as $val) {
                if ($val['isOut'] !='是'){
                    $material         = query('material', " id = '$array[materialId]'");
                    $field            = [
                        'id'                 => self::getId(),
                        'coid'               => self::$coid,
                        'recordDate'         => $info['recordDate'],
                        'classify'           => '生产派工',
                        'type'               => $info['type'],
                        'routingMxId'        => $val['id'],
                        'routingId'          => $array['routingId'],
                        'lineId'             => $info['lineId'],
                        'machiningOrderId'   => $info['id'],
                        'machiningOrderMxId' => $array['id'],
                        'materialId'         => $array['materialId'],
                        'unit'               => $array['unit'],
                        'num'                => self::formattingFigure($array['num'], 'numPrecision'),
                        'processId'          => $val['processId'],
                        'startDate'          => $info['startDate'],
                        'endDate'            => $info['endDate'],
                        'state'              => '草稿',
                        'stid'               => self::$stid,
                    ];
                    $userNo           = codeTemplateClass::generateCode('machiningSendOrder', [
                        'prefix' => 'SCPG' . date('Ymd',strtotime($field['recordDate'])),
                        'table'  => 'machining_send_order',
                        'field'  => 'orderNo',
                        'where'  => " and coid = '" . self::$coid . "'",
                    ], $field);
                    $field['orderNo'] = $userNo;
                    $warn             = static::insert(array(
                        'table' => 'machining_send_order',
                        'field' => $field,
                    ));
                    if ($warn != '新增成功') {
                        return "产品【{$material['code']}-{$material['name']}-{$material['model']}】生成派工单失败";
                    }
                }
            }
        }
        return true;
    }

    /**
     * 撤回
     */
    public function cancelEdit()
    {
        $id    = $this->post('id');
        $info  = query(self::table, " id = '$id'");
        $field = [
            'state'       => '草稿',
            'checkId'     => '',
            'confirmTime' => '',
        ];
        if (!$this->power(self::page, 'cancel')) {
            $json['warn'] = '你还没有撤回权限';
        } elseif (empty($info['id'])) {
            $json['warn'] = '未找到此生产计划';
        } elseif ($info['state'] != '已确认') {
            $json['warn'] = '此生产计划不是已确认状态，无需撤回';
        } elseif (static::total(array('table' => 'pick_order_detail', "where" => "sourceId in (select id from machining_order_detail where orderId = '$info[id]')  and coid = '" . self::$coid . "'")) > 0) {
            $json['warn'] = '已有领料单或退料单关联此生产计划，不能撤回';
        } elseif (static::total(array('table' => 'machining_in_order_detail', "where" => "sourceId in (select id from machining_order_mx where orderId = '$info[id]')  and coid = '" . self::$coid . "'")) > 0) {
            $json['warn'] = '已有生产入库单关联此生产计划，不能撤回';//生产入库单
        } elseif (static::total(array('table' => 'machining_send_order', "where" => "machiningOrderId= '$info[id]' and classify = '生产派工' and coid = '" . self::$coid . "'")) > 0) {
            $json['warn'] = '已有生产派工单关联此生产计划，不能撤回';
        } elseif (static::total(array('table' => 'machining_send_order', "where" => "machiningOrderId= '$info[id]' and classify = '委外派工'  and coid = '" . self::$coid . "'")) > 0) {
            $json['warn'] = '已有委外派工单关联此生产计划，不能撤回';
        } elseif (static::total(array('table' => 'process_report', "where" => "machiningOrderId= '$info[id]' and sourceTable = 'machining_card_detail'  and coid = '" . self::$coid . "'")) > 0) {
            $json['warn'] = '已有生产流转卡关联此生产计划，不能撤回';//工序报工单
        } else {
            $json['warn'] = update(array(
                "table" => self::table,
                "field" => $field,
                "where" => "id = '$info[id]'",
            ));
            if (in_array($json['warn'], ["新增成功", "更新成功"])) {
                delete(array('table' => 'outsource_demands', 'where' => "sourceId = '$info[id]'"));
                delete(array('table' => 'product_demands', 'where' => "sourceId = '$info[id]'"));
                delete(array('table' => 'machining_demands', 'where' => "sourceId = '$info[id]'"));
                delete(array('table' => 'semi_demands', 'where' => "sourceId = '$info[id]'"));
                delete(array('table' => 'ingredients_demands', 'where' => "machiningOrderId = '$info[id]'"));
                delete(array('table' => 'machining_card_detail', 'where' => "orderId in (select id from machining_card where sourceId = '$info[id]')"));
                delete(array('table' => 'machining_card', 'where' => "sourceId = '$info[id]'"));
                self::insertOperateLog($info['id'],self::title,"撤回");
                //添加日志
                $type = mb_substr($json['warn'], 0, 2, 'utf-8');
                $text = $this->control['name'] . $type . "了" . self::title . "id：【" . $id . '】';
                $this->logText(["type" => self::page, "text" => $text]);
                //返回
                $_SESSION['warn'] = $json['warn'];
                $json['warn']     = 2;
                $json['href']     = root . "control/adIndex.html?cla=" . $this->get('cla') . "&fun=detail&id=" . $info['id'];
            }
        }
        resp:
        return json_encode($json, 320);
    }
    /**
     * Desc : 提交前数据验证
     * User : you name
     * Date : 2023-11-17 16:04
     * @param $info
     * @return string|bool
     */
    public function dataValidation($info): string|bool
    {
        $annotation      = [
            'type'       => '生产计划类型',
            'lineId'     => '生产线',
            'recordDate' => '单据日期',
            'startDate'  => '计划开工日期',
            'endDate'    => '计划完工日期',
        ];
        $rules           = [
            'type'       => 'required',
            'lineId'     => 'required',
            'recordDate' => 'required|date',
            'startDate'  => 'required|date',
            'endDate'    => 'required|date',
        ];
        // 校验是否符合规则
        $vt     = new \verify();
        $result = $vt->setAliases($annotation)->validate($info, $rules)->isOk();
        if ($result === false) {
            return $vt->getErrs()[0];  // 多个规则提示最前面一条
        }
        if (strtotime($info['endDate']) < strtotime($info['startDate'])) {
            return '生产计划开工时间不能小于完工时间';
        }
        $result = find(array('table' => self::table_material, 'field' => 'id,materialId,num,unit,routingId', 'where' => "orderId = '$info[id]' order by list"));
        if(count($result['array']) == 0){
            return "请选择需要生产的产品";
        }
        if(static::is_null_array(array_column($result['array'],'num'),false)){
            return "产品明细中【计划数量】不能为空，且必须大于0";
        }
        if(static::is_null_array(array_column($result['array'],'unit'))){
            return "产品明细中【单位】不能为空";
        }
        if(static::is_null_array(array_column($result['array'],'routingId'))){
            return "产品明细中【工艺路线】不能为空";
        }
        $withOutMaterial = optionSettingClass::getSettingItem('生产', 'withOutMaterial');
        $result = find(array('table' => self::tableMx, 'field' => 'id,materialId,num,unit', 'where' => "orderId = '$info[id]' order by list"));
        if(!$withOutMaterial && count($result['array']) == 0){
            return "未找到所需物料";
        }
        if(!$withOutMaterial && static::is_null_array(array_column($result['array'],'materialId'))){
            return "所需物料中物料不能为空";
        }
        if(!$withOutMaterial && static::is_null_array(array_column($result['array'],'unit'))){
            return "所需物料中【单位】不能为空";
        }
        if(!$withOutMaterial && static::is_null_array(array_column($result['array'],'num'),false)){
            return "所需物料中【额定数量】不能为空，且必须大于0";
        }

        return true;
    }
    /**
     * 提交
     */
    public function commitEdit()
    {
        $id    = $this->post('id');
        $info  = query(self::table, " id = '$id'");
        $field = [
            'state' => '待确认',
        ];
        //提交前数据验证
        $json['warn'] = $this->dataValidation($info);
        if($json['warn'] !== true){
            goto resp;
        }
        $warn = '';
        if (optionSettingClass::getSettingItem('销售', 'machiningStrick')) {
            //控制是否按订单严格生产
            $result = find(array('table' => self::table_material, "where" => "orderId = '$id' order by list asc"));
            foreach ($result['array'] as $array) {
                if ($array['sourceId'] && $array['sourceTable']) {
                    $material    = query('material', "id = '$array[materialId]'");
                    $sourceOrder = query($array['sourceTable'], "id = '$array[sourceId]'");
                    //已进入生产计划的单据数量
                    $occupyNum = $this->occupy($info['id'], $array['sourceId'], $array['materialId'], $material['unit']);
                    $num       = machiningOrderClass::material_num_change($array['materialId'], $array['unit'], $material['unit'], $array['num']);
                    if (self::formattingFigure($occupyNum + $num, 'numPrecision') > $sourceOrder['num']) {
                        $warn = "物料【{$material['name']}】已超出订单数量";
                        break;
                    }
                }
            }
        }

        if (!$this->power(self::page, 'edit')) {
            $json['warn'] = '你还没有编辑权限';
        } elseif (empty($info['id'])) {
            $json['warn'] = '未找到此生产计划';
        } elseif ($info['state'] != '草稿') {
            $json['warn'] = '此生产计划不是草稿状态，不能提交';
        } elseif ($warn) {
            $json['warn'] = $warn;
        } else {
            $this->startTrans();
            $json['warn'] = update(array(
                "table" => self::table,
                "field" => $field,
                "where" => "id = '$info[id]'",
            ));
            if (in_array($json['warn'], ["新增成功", "更新成功"])) {
                // 单据是否需要复核
                if(!in_array(self::page,self::$publicSettings['documentReview'])){
                    $resultJson = $this->confirmEdit();
                    $json = json_decode($resultJson,true);
                    if ($json['warn'] != 2){
                        $this->rollback();
                        goto resp;
                    }
                }else {
                    self::insertOperateLog($info['id'],self::title,"提交");
                }
                $this->commitTrans();
                //添加日志
                $type = mb_substr($json['warn'], 0, 2, 'utf-8');
                $text = $this->control['name'] . $type . "了" . self::title . "id：【" . $id . '】';
                $this->logText(["type" => self::page, "text" => $text]);
                //返回
                $_SESSION['warn'] = $json['warn'];
                $json['warn']     = 2;
                $json['href']     = root . "control/adIndex.html?cla=" . $this->get('cla') . "&fun=detail&id=" . $info['id'];
            } else {
                $this->rollback();
            }
        }
        resp:
        return json_encode($json, 320);
    }

    /**
     * 计算已进入生产的订单数量
     * @param $id string 领料单ID
     * @param $sourceId
     * @param $materialId
     * @param $unit
     * @return float
     */
    private function occupy(string $id, $sourceId, $materialId, $unit): float
    {
        $pickOrderMx = find(array('table' => self::table_material . " as a join " . self::table . " as b on a.orderId = b.id", 'where' => "a.sourceId = '$sourceId' and a.coid = '" . self::$coid . "' and a.materialId = '$materialId' and b.id !='$id'", 'field' => "a.num,a.unit"));
        $pickNum     = 0;
        foreach ($pickOrderMx['array'] as $array) {
            $pickNum += machiningOrderClass::material_num_change($materialId, $array['unit'], $unit, $array['num']);
        }
        return self::formattingFigure($pickNum, 'numPrecision');
    }

    // 操作中转
    public function operate()
    {
        $act  = $this->post('act') ?? '';
        $pwd  = $word = false; // 需要密码
        $form = $power = '';
        switch ($act) {
            case 'confirm':
                $act      = 'confirm';
                $msg      = '确认';
                $title    = '是否已确认此生产计划，确认后将不可修改？';
                $function = 'confirmEdit';
                $form     = self::page . 'OperateForm, detailForm';
                $power    = '';
                $word     = true;
                break;
            case 'commit':
                $act      = 'edit';
                $msg      = '编辑';
                $title    = '是否确认提交此生产计划？';
                $function = 'commitEdit';
                $form     = self::page . 'OperateForm, detailForm';
                $power    = '';
                $word     = true;
                break;
            case 'cancel':
                $act      = 'cancel';
                $msg      = '撤回';
                $title    = '是否确认撤回此生产计划？';
                $function = 'cancelEdit';
                $form     = self::page . 'OperateForm, detailForm';
                $power    = '';
                $word     = true;
                break;
            case 'close':
                $act      = 'edit';
                $msg      = '关闭';
                $title    = '是否确认批量关闭选中的生产计划单？';
                $function = 'closeEdit';
                $form     = self::page . 'OperateForm, listForm, quoForm';
                $power    = '';
                $word     = true;
                break;
            case 'batchDel':
                $act      = 'del';
                $msg      = '删除' . self::title;
                $title    = self::page . 'Delete';
                $function = 'batchDel';
                $form     = self::page . 'OperateForm, listForm, quoForm';
                $power    = '';
                break;
            default:
                $act      = 'del';
                $msg      = '删除' . self::title;
                $title    = self::page . 'Delete';
                $function = 'delete';
                $form     = self::page . 'OperateForm, listForm, quoForm';
                $power    = '';
                break;
        }
        if (!$this->power(self::page, $act)) {
            $_SESSION['warn'] = '你还没有' . $msg . '的权限';
            $json             = [
                'warn' => 2,
                'href' => root . 'control/adIndex.html?cla=' . $this->get('cla') . '&fun=home',
            ];
        } else {
            return $this->confirm([
                'title'    => $title,
                'function' => $function,
                'form'     => $form,
                'power'    => $power,
                'pwd'      => $pwd,
                'word'     => $word,
            ]);
        }

        resp:
        return json_encode($json, 320);
    }

    /**
     * 删除
     *
     * @return bool|string
     */
    public function delete()
    {
        $array = $this->post('listId') ?? $this->post('id');
        if (!$this->power(self::page, "del")) {
            $json['warn'] = "你没有删除的权限";
        } elseif (empty($array)) {
            $json['warn'] = "你一个数据都没有选择呢";
        } else {
            $text  = "";
            $x     = 0;
            $array = is_array($array) ? $array : [$array];
            foreach ($array as $id) {
                $info = query(self::table, " id = '$id' ");
                if ($info['state'] == '已确认') {
                    $warn = "删除失败：已确认，暂不能删除";
                } else {
                    //最后删除基本参数
                    delete(array("table" => self::table, "where" => " id = '$id' "));
                    delete(array("table" => self::tableMx, "where" => " orderId = '$id' "));
                    delete(array("table" => self::table_material, "where" => " orderId = '$id' "));
                    delete(array("table" => "auditing_follow", "where" => " typeId = '$id' "));
                    //返回
                    $warn = "删除成功";
                    $x++;
                }
                $text .= self::title . "ID【" . $info['id'] . "】" . $warn . "。<br>";
            }
            //添加日志
            $text = $this->control['name'] . "删除了" . self::title . "，结果如下：<br>" . $text;
            $this->logText(array("type" => self::page, "text" => $text));
            $_SESSION['warn'] = "删除了" . $x . "个" . self::title . "，详情见系统日志。";
            $json['warn']     = 2;
            $json['href']     = root . "control/adIndex.html?cla=" . $this->get('cla') . "&fun=home";
        }
        return json_encode($json, 320);
    }

    /**
     * 列表删除
     */
    public function batchDel(): bool|string
    {
        $listId = $this->post('listId');
        if (!$this->power(self::page, "del")) {
            $json['warn'] = "你没有删除" . self::title . "的权限";
        } elseif (empty($listId) || !is_array($listId)) {
            $json['warn'] = '您还没有选择单据';
        } else {
            $x    = 0;
            $text = '';
            $all  = find(array(
                'table' => 'machining_order_mx',
                'field' => "distinct orderId",
                "where" => "coid = '" . self::$coid . "' and id in ('" . implode("','", $listId) . "')"
            ));
            //草稿状态下的生产计划，未完善产品明细
            $order  = find(array(
                'table' => 'machining_order',
                'field' => "id as orderId",
                "where" => "coid = '" . self::$coid . "' and state = '草稿' and id in ('" . implode("','", $listId) . "')"
            ));
            $all = array_merge($all['array'],$order['array']);
            foreach ($all as $array) {
                $machiningOrder = query(self::table, "id = '$array[orderId]'");
                if ($machiningOrder['state'] == '已确认') {
                    $warn = "删除失败：已确认，暂不能删除";
                } else {
                    //最后删除基本参数
                    delete(array("table" => self::table, "where" => " id = '$array[orderId]' "));
                    delete(array("table" => self::tableMx, "where" => " orderId = '$array[orderId]' "));
                    delete(array("table" => self::table_material, "where" => " orderId = '$array[orderId]' "));
                    delete(array("table" => "auditing_follow", "where" => " typeId = '$array[orderId]' "));
                    //返回
                    $warn = "删除成功";
                    $x++;
                }
                $text .= self::title . "ID【" . $machiningOrder['id'] . "】,单号【{$machiningOrder['orderNo']}】" . $warn . "。<br>";
            }
            $text = $this->control['name'] . "删除了" . self::title . "，结果如下：<br>" . $text;
            $this->logText(["type" => self::page, "text" => $text]);
            $json['warn'] = 2;
            $json['msg']  = "删除了" . $x . "个" . self::title . "，详情见系统日志。";
        }
        return json_encode($json, 320);
    }

    /**
     * 批量打印
     *
     * @return string
     */
    public function batchPrint(): string
    {
        $lists = $this->get('lists');
        if (empty($lists)) {
            exit('参数错误');
        } else {
            $listId = base64_decode($lists, true);
            $array  = explode(',', $listId);
            $data   = [];
            foreach ($array as $id) {
                $data[] = static::printPage($id, false);
            }
            return (new printTemplateClass())->start(self::page, $data);
        }
    }

    /**
     * 打印
     *
     * @return string
     */
    public function printPage($param = '', $isReturn = true): string|array
    {
        $id = $param ?: $this->get('id');
        if ($param) {
            $mx = query(self::table_material, "id = '$param'");
            $id = $mx['orderId'];
        }
        $info                 = query(self::table, " id = '$id'");
        $line                 = query('product_line', "id = '$info[lineId]'");
        $info['companyName']  = query('company', "coid = '" . self::$coid . "'")['name']; //公司名称//公司名称
        $info['title']        = '生产计划单';
        $info['lineName']     = $line['workshop'] . $line['name'];
        $info['materialName'] = '产品明细';
        $info['useMaterialName'] = '原材料';
        $info['printTime']    = $this->time;
        $info['stid']         = self::findStaff($info['stid']);
        $info['qr']           = "source=" . self::table . "&id=" . $info['id'];
        $info['tags']         = orderTagClass::getTagName($info['id']);
        $detail               = find(['table' => self::table_material, 'where' => "orderId = '$info[id]' order by list asc"])['array'];
        $items                = [];
        $x                    = 1;
        $totalNum             = 0;
        foreach ($detail as $array) {
            $material    = query('material', "id = '$array[materialId]'");
            $relativeNo  = '';
            if ($array['sourceTable'] == 'product_demands') {
                $productDemand = query('product_demands', "id = '$array[sourceId]'");
                $sale_order    = query('sale_order', "id = '$productDemand[sourceId]'");
                $relativeNo    = $sale_order['orderNo'];
            } elseif ($array['sourceTable'] == 'sale_refund_order_detail') {
                $saleRefundOrderMx = query('sale_refund_order_detail', "id = '$array[sourceId]'");
                $saleRefundOrder   = query('sale_refund_order', "id = '$saleRefundOrderMx[orderId]'");
                $relativeNo        = $saleRefundOrder['orderNo'];
            } elseif ($array['sourceTable'] == 'sale_order_detail') {
                $saleOrderMx = query('sale_order_detail', "id = '$array[sourceId]'");
                $saleOrder   = query('sale_order', "id = '$saleOrderMx[orderId]'");
                $relativeNo  = $saleOrder['orderNo'];
            }
            $items[] = [
                'no'              => $x,
                'relativeNo'      => $relativeNo,
                'materialCode'    => $material['code'],
                'materialName'    => $material['name'],
                'model'           => $material['model'],
                'unit'            => $array['unit'],
                'text'            => $array['text'],
                'num'             => self::lostZero($array['num'], 'numPrecision'),
            ];
            $x++;
            $totalNum += $array['num'];
        }
        $append         = [
            'no'  => '合计',
            'num' => self::lostZero($totalNum, 'numPrecision'),
        ];
        $items[]        = $append;
        $info['detail'] = $items;
        $result   = find(array('table' => self::tableMx, 'where' => "orderId = '$info[id]' order by list"));
        $mxArray=[];
        foreach ($result['array'] as $index=>$array) {
            $material         = query('material', " id = '$array[materialId]'");
            $mxArray[] = [
                'no'              => $index+1,
                'code'    => $material['code'],
                'name'    => $material['name'],
                'model'           => $material['model'],
                'unit'            => $array['unit'],
                'baseNum'            =>  self::lostZero($array['num'], 'numPrecision'),
                'loss'            =>$array['loss'] * 100,
                'num'            => self::lostZero($array['quantity'], 'numPrecision'),
                'text'            => $array['text'],
            ];
        }
        $info['infoMx'] =$mxArray;
        if ($isReturn) {
            return (new printTemplateClass())->start(self::page, $info);
        } else {
            return $info;
        }
    }

    /**
     * 获取默认的工艺路线
     */
    public function defaultRouting($materialId, $type = '物料')
    {
        if (empty($materialId)) {
            return '';
        }
        if ($type == '物料') {
            $material = query('material', "id = '$materialId'");
            $routing = query(
                'routing',
                "coid = '" . self::$coid . "' and (materialId like '%{$material['id']}%' or (materialTypeId in ('" . implode("','", explode('/',$material['typePath'])) . "') and materialId = '')) and type = 1 and state = '启用' order by isDefault desc"
            );
            if (!empty($routing['id'])) {
                return $routing['isDefault'] ? $routing['id'] : '';//有这个物料的工艺路线但不是默认，则返回空，因为存在多条不是默认的工艺路线，不知道取哪一条
            }else {
                //$material = query('material', "id = '$materialId'");
                //return $this->defaultRouting($material['typeId'], '分类');
                return '';
            }
        } else {
            //分类
            $materialType = query('material_type', "id = '$materialId'");
            $routing      = query('routing', "coid = '" . self::$coid . "' and materialTypeId = '$materialType[id]' and materialId = '' and type = 1 and state = '启用' order by isDefault desc");
            if (!empty($routing['id'])) {
                return $routing['isDefault'] ? $routing['id'] : '';//有这个分类的工艺路线但不是默认，则返回空，因为存在多条不是默认的工艺路线，不知道取哪一条
            } else {
                return $this->defaultRouting($materialType['pid'], '分类');
            }
        }
    }

    /*列表页-查询条件*/
    public function semiOrderOptions($isAsync = true)
    {
        //生成查询表单
        $html = $this->tableShow([
            [
                [
                    "单据编号" => text(array("name" => "orderNo")),
                    "物料编码" => text(array("name" => "code")),
                    "物料名称" => text(array("name" => "materialName")),
                ],
                [
                    '单据日期' => text(array("name" => "createTime", 'laydate' => ['type' => 'date', 'range' => '~'])).
                        hidden(array('name' => 'id', 'value' => $this->get('id'))),
                ]
            ]
        ],1);
        //返回
        if (!$isAsync) {
            return $html;
        } else {
            return json_encode(['html' => $html], 320);
        }
    }

    /**
     * 半成品进度
     * @return false|string
     */
    public function semiOrder()
    {
        $html = $this->semiOrderOptions(false);
        //控件
        $button = "";
        $json['html'] = $this->search($html, layout: 'pop', toolButton: $button);
        return json_encode($json, 320);
    }

    public function semiOrderSearch()
    {
        $id    = $this->post('id');
        $equal = [
            'a.coid' => self::$coid,
        ];
        $like  = [
            "a.orderNo"      => $this->post('orderNo'),
            "b.code"         => $this->post('code'),
            "b.materialName" => $this->post('materialName'),
        ];
        $and   = " and a.sourceId in (select id from semi_demands where sourceId = '$id')";
        if ($this->post('createTime')) {
            $and .= self::timeComparison($this->post('createTime'), field: 'c.recordDate');
        }
        //生成查询语句
        $where = search([
                "="    => $equal,
                "like" => $like,
            ]) . $and;
        //获取记录
        //排序
        $orderBy = $this->listSort(array(
            "search" => $this->post('list_sort_key') ?: 'c.recordDate desc,a.list',
            "sort"   => $this->post('list_sort') ?: "desc",
            "array"  => array(),
        ));
        $page = $this->page([
            "table" => 'machining_order_mx as a join material as b on a.materialId = b.id join machining_order as c on a.orderId = c.id',
            "where" => $where,
            'field' => "a.*,b.name,b.code,b.model,b.subject,c.orderNo,c.recordDate,b.supplyControl,b.source",
            "order" => $orderBy['orderBy']
        ]);
        //获取自定义字段参数
        $installSearch = $this->installSearch();
        $body          = [];
        //表格控件区
        $liButton = array();
        $url  = root . 'control/adIndex.html?cla=';
        foreach ($page['array'] as $array) {
            //已生产入库的数量
            $inStockNum = $this->inStockNum($array['id'], $array['unit']);
            //本行初始化
            $row = [];
            foreach ($installSearch['key'] as $key) {
                switch ($key) {
                    case "orderNo":
                        $row[] = componentClass::urlTitle($array['orderNo'], $url . 'semiMachiningOrder&fun=detail&id=' . $array['orderId'], '半成品加工单');
                        break;
                    case "num":
                        $row[] = self::lostZero($array['num'], 'numPrecision');
                        break;
                    case "inventoryNum":
                        $inventoryNum = self::inventory($array['materialId'], $array['unit']);
                        $row[] = self::lostZero($inventoryNum, 'numPrecision');
                        break;
                    case "inStockNum":
                        $row[] = self::lostZero($inStockNum, 'numPrecision');
                        break;
                    case "residue":
                        $row[] = self::lostZero($array['num'] - $inStockNum, 'numPrecision');
                        break;
                    default:
                        $row[] = isset($array[$key]) && $array[$key] ? $array[$key] : '';
                }
            }
            $body[] = $row;
        }
        //控件
        //生成页面
        $li = $this->li(array(
            "body"        => $body,
            "install"     => $installSearch,
            "button"      => array("html" => $liButton),
            "orderBySign" => $orderBy,
            "page"        => $page['para'],
        ));
        $json['html'] = $page['hint'] . $li . $page['button'];
        return json_encode($json, 320);
    }

    /*列表页-查询条件*/
    public function qualityTrackOptions($isAsync = true)
    {
        //生成查询表单
        $html = $this->tableShow([
            [
                [
                    "状态" => checkbox(array('name' => 'state', 'value' => ['待质检', '已质检'], 'checked' => ['待质检'])),
                ],
                [
                    "单据编号" => text(array("name" => "orderNo")),
                    "生产线"   => selectMultiple(["name" => "lineId", 'table' => "product_line", 'sqlId' => 'id', 'where' => "coid = '" . self::$coid . "'", 'sqlField' => 'name']),
                    '物料编码' => text(array("name" => "code")),
                ],
                [
                    '物料名称' => text(array("name" => "materialName")),
                    '不良原因' => text(array("name" => "reason")),
                    '处置方式'  => repeatSelect(array('name' => 'method', 'sqlForm' => " quarantine_reason where coid = '" . self::$coid . "'", 'sqlField' => 'method')),
                ],
                [
                    "质检日期" => text(array("name" => "createTime", 'laydate' => ['type' => 'date', 'range' => '~'])).
                        hidden(array('name' => 'id', 'value' => $this->get('id'))),
                ]
            ]
        ],1);
        //返回
        if (!$isAsync) {
            return $html;
        } else {
            return json_encode(['html' => $html], 320);
        }
    }

    public function qualityTrack()
    {
        $html = $this->qualityTrackOptions(false);
        //控件
        $button = "";
        $json['html'] = $this->search($html, layout: 'pop', toolButton: $button);
        return json_encode($json, 320);
    }

    public function qualityTrackSearch()
    {
        $id    = $this->post('id');
        $equal = [
            "a.targetId" => $this->post('lineId'),
            'a.coid'     => self::$coid,
        ];
        $like  = [
            "a.orderNo"      => $this->post('orderNo'),
            "b.code"         => $this->post('code'),
            "b.materialName" => $this->post('materialName'),
        ];
        $and   = " and a.type in (2,4) and (sourceMxId in (select id from machining_in_order_detail where sourceId in (select id from machining_order_mx where orderId = '$id'))
         or sourceId in (select id from process_report where machiningOrderId = '$id'))";
        if ($this->post('state')) {
            //单据日期
            $and .= " and a.state in ('" . implode("','", $this->post('state')) . "')";
        }
        //生成查询语句
        $where = search([
                "="    => $equal,
                "like" => $like,
            ]) . $and;
        //排序
        $orderBy = $this->listSort(array(
            "search" => $this->post('list_sort_key') ?: 'a.list',
            "sort"   => $this->post('list_sort') ?: "desc",
            "array"  => array(),
        ));
        $page = $this->page([
            "table" => 'quality_order as a join material as b on a.materialId = b.id',
            "where" => $where,
            'field' => "a.*,b.name,b.code,b.model",
            "order" => $orderBy['orderBy']
        ]);
        //获取自定义字段参数
        $installSearch = $this->installSearch();
        $body          = [];
        //表格控件区
        $liButton = array("");
        $url  = root . 'control/adIndex.html?cla=';
        foreach ($page['array'] as $array) {
            $cla      = $array['type'] == 2 ? 'processInspection' : 'processingInspection';
            $dominate = [
                "box"  => [
                    "name"  => "id",
                    "value" => $array['id']
                ],
                'edit' => ["title" => "编辑", "click" => "newIframe('" . self::title . "详情','" . $url . $cla . "&fun=detail&id=" . $array['id'] . "')"],
            ];
            $sourceInfo  = [];
            $processName = '';
            if ($array['type'] == 2) {
                $sourceInfo = query('machining_in_order', "id = '$array[sourceId]'");
            } elseif ($array['type'] == 4) {
                $sourceInfo               = query('process_report', "id = '$array[sourceId]'");
                $sourceInfo['recordDate'] = $sourceInfo['teamNo'];
                $process                  = query('process', "id = '$sourceInfo[processId]'");
                $processName              = $process['name'];
            }
            $liButton[] = $this->dominate($dominate);
            //本行初始化
            $row = [];
            foreach ($installSearch['key'] as $key) {
                switch ($key) {
                    case "type":
                        $type     = $array['type'] == 2 ? '制终检验' : '制程检验';
                        $row[] = $type;
                        break;
                    case "time":
                        $row[] = date('Y-m-d', strtotime($array['time']));
                        break;
                    case "orderNo":
                        $row[] = $sourceInfo['orderNo'];
                        break;
                    case "lineName":
                        $line     = query('product_line', "id = '$array[targetId]'");
                        $lineName = $line['workshop'] . '-' . $line['name'];
                        $row[] = $lineName;
                        break;
                    case "processName":
                        $row[] = $processName;
                        break;
                    case "num":
                    case "passNum":
                        $row[] = self::lostZero($array[$key], 'numPrecision');
                        break;
                    case "badNum":
                        $badNum   = 0;
                        if ($array['state'] == '已质检') {
                            $badNum = self::lostZero($array['num'] - $array['passNum'], 'numPrecision');
                        }
                        $row[] = $badNum;
                        break;
                    default:
                        $row[] = isset($array[$key]) && $array[$key] ? $array[$key] : '';
                }
            }
            $body[] = $row;
        }
        //控件
        //生成页面
        $li = $this->li(array(
            "body"        => $body,
            "install"     => $installSearch,
            "button"      => array("html" => $liButton),
            "orderBySign" => $orderBy,
            "page"        => $page['para'],
        ));
        $json['html'] = $page['hint'] . $li . $page['button'];
        return json_encode($json, 320);
    }


    /**
     * 物料追溯
     * @return false|string
     */
    public function materialTrack()
    {
        $bomArray = $this->getFactMaterialNum($this->get('id'));
        $tr[]     = array('存货类别', "物料编码", "物料名称", "规格型号", "单位", '额定数量', "损耗率(%)", '计划用量', '实际BOM用量', '实际领料量', '实际退料量', '实际用量', '备注');
        $sql      = find(array("table" => "machining_order_detail", "where" => "orderId = '" . $this->get('id') . "' order by list asc "));
        foreach ($sql['array'] as $array) {
            $material    = query('material', "id = '$array[materialId]'");
            //领料

            $pickNum      = 0;
            $pickOrderAll = find(array(
                'table' => 'pick_order as a join pick_order_detail as b on a.id = b.orderId',
                "where" => "a.state = '已确认' and a.type=0 and b.sourceId = '$array[id]' and b.materialId = '$array[materialId]'"
            ));
            foreach ($pickOrderAll['array'] as $val) {
                $pickNum += machiningOrderClass::material_num_change($array['materialId'], $val['unit'], $array['unit'], $val['num']);
            }
            $pickNum = self::formattingFigure($pickNum, 'numPrecision');
            //退料
            $refundNum      = 0;
            $refundOrderAll = find(array(
                'table' => 'pick_order as a join pick_order_detail as b on a.id = b.orderId',
                "where" => "a.state = '已确认' and a.type=1 and b.sourceId = '$array[id]' and b.materialId = '$array[materialId]'"
            ));
            foreach ($refundOrderAll['array'] as $val) {
                $refundNum += machiningOrderClass::material_num_change($array['materialId'], $val['unit'], $array['unit'], $val['num']);
            }
            $refundOrderAll = find(array(
                'table' => 'pick_order as a join pick_order_detail as b on a.id = b.orderId',
                "where" => "a.state = '已确认' and a.type=1 and b.sourceId in (select id from pick_order_detail where sourceId = '$array[id]') and b.materialId = '$array[materialId]'"
            ));
            foreach ($refundOrderAll['array'] as $val) {
                $refundNum += machiningOrderClass::material_num_change($array['materialId'], $val['unit'], $array['unit'], $val['num']);
            }
            $refundNum = self::formattingFigure($refundNum, 'numPrecision');

            $bomNum = 0;//实际BOM用量
            $mxAll  = find(array('table' => 'machining_order_mx', 'where' => "orderId = '" . $this->get('id') . "'"));
            foreach ($mxAll['array'] as $item) {
                $bomNum += $this->getMaterialBom($bomArray, $item['id'], $array['materialId']);
            }

            $tr[] = array(
                $material['subject'],
                $material['code'],
                $material['name'],
                $material['model'],
                $array['unit'],
                self::lostZero($array['num'], 'numPrecision'),
                ($array['loss'] * 100) . '%',
                self::lostZero($array['quantity'], 'numPrecision'),
                $bomNum,
                $pickNum,
                $refundNum,
                $pickNum - $refundNum,
                $array['text']
            );
        }

        //材料分配明细
        $disTr[] = ['存货类别', '物料编码', '物料名称', '规格型号', '单位', '实际BOM用量', '实际分配用量'];
        $all     = find(array('table' => 'material_distribute_num', 'where' => "machiningOrderId = '" . $this->get('id') . "' order by list"));
        foreach ($all['array'] as $array) {
            $material    = query('material', " id = '$array[materialId]'");
            $disTr[]     = [
                $material['subject'],
                $material['code'],
                $material['name'],
                $material['model'],
                $array['unit'],
                $this->getMaterialBom($bomArray, $array['machiningOrderMxId'], $array['materialId']),
                self::lostZero($array['num'], 'numPrecision')
            ];
        }
        //构建表单
        $json['html'] = "
            <div class='tab-content'>
                ".$this->detailList([
                    'title' => '领料明细',
                    'html' => $this->listTable(array("tr" => $tr, 'showSort' => true)),
                ]).
                $this->detailList([
                    'title' => '材料分配明细',
                    'html' => $this->listTable(array("tr" => $disTr, 'showSort' => true)),
                ])."
            </div>";
        //返回
        return json_encode($json, 320);
    }

    /**
     * 获取实际的用料数量组
     */
    public function getFactMaterialNum($id): array
    {
        $material_list = [];//实际入库的产品数量
        $all           = find(array(
            'table' => 'inventory_detail',
            'where' => "coid = '" . self::$coid . "' and sourceId in (select id from machining_in_order_detail where sourceId in (select id from machining_order_mx where orderId = '$id'))"
        ));
        foreach ($all['array'] as $array) {
            $machining_in_order_detail                = query('machining_in_order_detail', "id = '$array[sourceId]'");
            $machining_order_mx                       = query('machining_order_mx', "id = '$machining_in_order_detail[sourceId]'");
            $material                                 = query('material', "id = '$array[materialId]'");
            $material_list[$machining_order_mx['id']] = $material_list[$array['materialId']] ?? 0;
            $material_list[$machining_order_mx['id']] += machiningOrderClass::material_num_change($array['materialId'], $array['unit'], $material['unit'], $array['num']);
        }
        $bomArray = [];
        foreach ($material_list as $machiningOrderMxId => $num) {
            $machining_order_mx            = query('machining_order_mx', "id = '$machiningOrderMxId'");
            $bomArray[$machiningOrderMxId] = $this->merge_material(self::oneBomMx($machining_order_mx['bomId'], $machining_order_mx['materialId'], $num, $machining_order_mx['unit']), []);
        }
        return $bomArray;
    }

    /**
     * @param $oldArray
     * @param $newArray
     * @return array
     */
    public function mergeBomArray($oldArray, $newArray): array
    {

        $allArr   = array_merge($oldArray, $newArray);
        $bomArray = [];
        foreach ($allArr as $machiningOrderMxId => $item) {
            $temp = array();
            foreach ($item as $value) {
                list($p, $n) = array_values($value);
                $temp[$p] = array_key_exists($p, $temp) ? $temp[$p] + $n : $n;
            }
            $bomArray[$machiningOrderMxId] = $temp;
        }

        $result = [];
        foreach ($bomArray as $machiningOrderMxId => $array) {
            foreach ($array as $materialId => $num) {
                $result[$machiningOrderMxId][] = array('materialId' => $materialId, 'num' => $num);
            }
        }
        return $result;
    }

    /**
     * @param $allArr
     * @return array
     */
    public function singleBomArray($allArr): array
    {
        $temp = array();
        foreach ($allArr as $item) {
            foreach ($item as $value) {
                list($p, $n) = array_values($value);
                $temp[$p] = array_key_exists($p, $temp) ? $temp[$p] + $n : $n;
            }
        }
        return $temp;
    }

    /**
     * 获取物料SKU的实际BOM用量
     * @param $bomArray
     * @param $machiningOrderMxId
     * @param $materialId
     * @return int
     */
    public function getMaterialBom($bomArray, $machiningOrderMxId, $materialId): int
    {
        if (isset($bomArray[$machiningOrderMxId])) {
            foreach ($bomArray[$machiningOrderMxId] as $array) {
                if ($array['materialId'] == $materialId) {
                    return $array['num'];
                }
            }
        }

        return 0;
    }

    /**
     * 导入模板字段初始化
     * @return array[]
     */
    public function importFieldInit($classify=''): array
    {
        return match ($classify) {
            default=>  [
                [
                    'columnName'=>'单据日期',
                    'aliasName'=>'单据日期',

                    'isMust'=>true,
                ],
                [
                    'columnName'=>'生产线编号',
                    'aliasName'=>'生产线编号',

                    'isMust'=>true,
                ],
                [
                    'columnName'=>'生产线名称',
                    'aliasName'=>'生产线名称',

                    'isMust'=>false,
                ],
                [
                    'columnName'=>'计划开始时间',
                    'aliasName'=>'计划开始时间',

                    'isMust'=>true,
                ],
                [
                    'columnName'=>'计划完工时间',
                    'aliasName'=>'计划完工时间',

                    'isMust'=>true,
                ],
                [
                    'columnName'=>'单据备注',
                    'aliasName'=>'单据备注',

                    'isMust'=>false,
                ],
                [
                    'columnName'=>'明细类型',
                    'aliasName'=>'明细类型(产品明细/所需物料)',

                    'isMust'=>true,
                ],
                [
                    'columnName'=>'产品编码',
                    'aliasName'=>'产品编码',

                    'isMust'=>true,
                ],
                [
                    'columnName'=>'产品名称',
                    'aliasName'=>'产品名称',

                    'isMust'=>false,
                ],
                [
                    'columnName'=>'单位',
                    'aliasName'=>'单位',

                    'isMust'=>true,
                ],
                [
                    'columnName'=>'计划数量/额定数量',
                    'aliasName'=>'计划数量/额定数量',

                    'isMust'=>true,
                ],
                [
                    'columnName'=>'生产批次/损耗率',
                    'aliasName'=>'生产批次/损耗率%',

                    'isMust'=>false,
                ],
                [
                    'columnName'=>'BOM编号',
                    'aliasName'=>'BOM编号',

                    'isMust'=>true,
                ],
                [
                    'columnName'=>'BOM版本',
                    'aliasName'=>'BOM版本',

                    'isMust'=>false,
                ],
                [
                    'columnName'=>'工艺路线编号',
                    'aliasName'=>'工艺路线编号',

                    'isMust'=>true,
                ],
                [
                    'columnName'=>'工艺路线名称',
                    'aliasName'=>'工艺路线名称',

                    'isMust'=>false,
                ],
                [
                    'columnName'=>'备注',
                    'aliasName'=>'备注',

                    'isMust'=>false,
                ],
            ]
        };
    }
}
